Eve Application Development

Michael L Brereton - 02 January 2008, http://www.ewesoft.com/

 

Beginners Guide - Contents

<< Previous: Laying Out Controls

>> Next: GUI Data Editing

Event Handling

Eve Application Development

Event Handling

The PRESSED ControlEvent and DATA_CHANGED DataChangeEvent

Event Handling Example

Event Listeners

Correctly using show() and exec()

Using newEventThread()/resumeEventThread()

Menu and List Events

SELECTED and DESELECTED Events

PRESSED Events

 

 

Like most GUI based systems, Eve uses an event-based model for receiving user input and for generating control actions. You react to user input in your applications by trapping and handling the appropriate events. The Events used by Eve are mostly contained in the package eve.ui.event.

 

The main events received by your controls will be pen/mouse events (as represented by a PenEvent object) and keyboard events (as represented by a KeyEvent object). These events are sent directly to a single Control object as determined by the Window manager. For PenEvents the Window manager determines the actual Control object being pressed and sends the appropriate event directly to it, adjusting x and y co-ordinates of the on-screen mouse/pen location to be relative to the target Control. For KeyEvents the Window manager determines which Control has the current keyboard “focus” and sends the KeyEvent directly to it.

 

In each case the appropriate Event is sent synchronously to the target by calling the Control’s postEvent() method.  This method will call the onEvent() method of the Control which it then uses to adjust its internal state and, if necessary, generate further Events in reaction to the user input.

 

The Events generated by the Control objects themselves are then passed to its parent container via a call to the parent’s onEvent() method. The event is passed all the way up the parent chain until it reaches the top-level Window. The parent can determine which control generated the Event by looking at the target member of the Event. Most of the events that you will be handling will be those generated by Controls in reaction to user input.

 

NOTE: Pen/Mouse and Key events are not passed up the Gui control tree unless you modify the Control to set the SendUpUIEvents modifier bit.

The PRESSED ControlEvent and DATA_CHANGED DataChangeEvent

There are two main event types that are generated by Control objects in reaction to user input and these are the two that you will be handling the most.

 

Some Controls, such as Button controls, do not have an internal state but will react to a pen press. When the appropriate user action is performed on the Control they generate a ControlEvent with the type of the Event set to ControlEvent.PRESSED.

 

Most other Controls, such as Input and CheckBox controls, keep some form of data as an internal state. User input may cause that state to change. These Controls generate a DataChangeEvent with the type of the Event set to DataChangEvent.DATA_CHANGED.

 

Using these two events alone, you will be able to handle the majority of user input in your programs. Note that some Controls (such as CheckBox controls) will generate both a PRESSED and DATA_CHANGED event, but you should handle only one of them.

 

An example of handling events is given below. It is a simple Form that takes as input two numbers and calculates another value from them. The numbers are either multiplied or divided depending on what the user chooses in the Choice.

 

You will note that the calculation is done and the result updated once the user changes any of the inputs. This is caused by handling the DataChange event.

Event Handling Example

 

package evesamples.ui;
 
import eve.sys.Convert;
import eve.sys.Event;
import eve.ui.*;
import eve.ui.event.ControlEvent;
import eve.ui.event.DataChangeEvent;
 
//##################################################################
public class Events extends Form{
//##################################################################
 
Input firstNumber, secondNumber, result;
Choice operation;
Button message,quit;
 
//===================================================================
public Events()
//===================================================================
{
     title = "Form Demo";
     resizable = true;
     addLast(firstNumber = new Input(),HSTRETCH,FILL);
     addLast(secondNumber = new Input(),HSTRETCH,FILL);
     addLast(result = new Input(),HSTRETCH,FILL);
     result.modify(DisplayOnly,0);
     addLast(operation = new Choice(new String[]{"Multiply","Divide"},0),HSTRETCH,FILL);
     addNext(message = new Button("Message"),HSTRETCH,FILL);
     addNext(quit = new Button("Exit"),HSTRETCH,FILL);
}
//-------------------------------------------------------------------
private void calculate()
//-------------------------------------------------------------------
{
     try{
             double one = Convert.toDouble(firstNumber.getText());
             double two = Convert.toDouble(secondNumber.getText());
             double answer = 
                     operation.getInt() == 0 ? one*two : one/two;
             result.setText(""+answer);
     }catch(Exception e){
             result.setText(e.getMessage());
     }
}
//===================================================================
public void onEvent(Event ev)
//===================================================================
{
     if (ev instanceof DataChangeEvent && ev.type == DataChangeEvent.DATA_CHANGED){
             calculate();
     }else if (ev instanceof ControlEvent && ev.type == ControlEvent.PRESSED){
             if (ev.target == quit){
                     Application.exit(0);
             }else if (ev.target == message){
                     new MessageBox("Hello","Hello there!",MBOK).execute();
             }
     }
     super.onEvent(ev); //Make sure you call this.
}
//##################################################################
}
//##################################################################
 

 

This example shows how you normally handle “Form-centric” data input. There is another method of handling input which is more “Object-centric” and uses a eve.ui.data.Editor to automatically update variables in an Object from data entered by the user. This is discussed in a later chapter.

Event Listeners

As we have seen, the event model allows parent Controls to capture events generated by their child controls. However it is also possible for an object which is not a container for a Control to handle the Control’s events. Any object which implements the EventListener interface can receive such events. You can do this by calling the addListener(EventListener listener) method on a Control. You can also call removeListener() later to remove the listener.

Correctly using show() and exec()

As mentioned before exec()/execute() display a Form modally and create a new Event Thread for a window if they are called within a window’s Event Thread. This allows the blocking of the old Event Thread but still allow the original window to receive repaint/resize events normally.

 

The show() method displays a Form non-modally. The new Form is displayed and the method returns immediately, but a new Event Thread for the old window will not be created. Therefore you should not block the event delivery thread if you call the show() method since there will be no other delivery thread to handling incoming user events.

 

Here is an example that shows how you can display new Forms and the correct and incorrect way to handle waiting for the Form to close.

 

import eve.sys.Event;
import eve.sys.EventListener;
import eve.ui.*;
import eve.ui.event.ControlEvent;
 
//##################################################################
public class ShowExec extends Form{
//##################################################################
 
//===================================================================
public ShowExec()
//===================================================================
{
     title = "Show/Exec Demo";
     
     Button b;
     addLast(b = new Button("Execute a Message Box!"));
     //
     //This functions correctly.
     //
     b.addListener(new EventListener(){
             public void onEvent(Event ev){
                     if (ev.type == ControlEvent.PRESSED){
                            Control c = ((Control)ev.target);
                            String txt = c.getText();
                            c.setText("Waiting...");
                            new MessageBox("Executed","This will execute() OK!",MBOK).execute(); 
                            c.setText(txt); 
                     }
             }
     });
     //
     //This functions correctly as well.
     //
     addLast(b = new Button("Show a Message Box!"));
     b.addListener(new EventListener(){
             public void onEvent(Event ev){
                     if (ev.type == ControlEvent.PRESSED){
                            Control c = ((Control)ev.target);
                            String txt = c.getText();
                            c.setText("Waiting...");
                            new MessageBox("Shown","This will show() OK!",MBOK).show();  
                            c.setText(txt); 
                     }
             }
     });
     //
     //This is wrong! It calls a show, which does NOT spawn a new event thread,
     //and then attempts to wait, which blocks all Gui events. It eventually gives
     //up and returns after 5 seconds.
     //
     addLast(b = new Button("Bad Show and Wait for Message Box!"));
     b.addListener(new EventListener(){
             public void onEvent(Event ev){
                     if (ev.type == ControlEvent.PRESSED){
                            Control c = ((Control)ev.target);
                            String txt = c.getText();
                            c.setText("Waiting...");
                            MessageBox mb = new MessageBox("Shown","This will show() OK!\nBut will block for 5 seconds.",MBOK);
                            mb.show();
                            //This will block this window's event thread for 5 seconds.
                            try{
                                    Form.waitUntilClosed(mb.handle,new eve.sys.TimeOut(5000));
                            }catch(Exception e){}
                            c.setText(txt); 
                            mb.exit(0);
                     }
             }
     });
     //
     // If you want to wait on a non-modal form to close or you
     // want to block for any other reason, you will have to call
     // newEventThread()
     //
     addLast(b = new Button("Good Show and Wait for Message Box!"));
     b.addListener(new EventListener(){
             public void onEvent(Event ev){
                     if (ev.type == ControlEvent.PRESSED){
                            final Control c = ((Control)ev.target);
                            final String txt = c.getText();
                            c.setText("Waiting...");
                            MessageBox mb = new MessageBox("Shown","This will show() OK!",MBOK);
                            mb.show();
                            if (false){
                                    //
                                    // This is one way to do it.
                                    //
                                    Object oldEvent = c.newEventThread(c);
                                    try{
                                           mb.waitUntilClosed();
                                    }finally{
                                           c.resumeEventThread(oldEvent);
                                    }
                            }else{
                                    //
                                    // This uses a convenience method to wait on a Handle
                                    //
                                    c.waitEventThread(mb.handle);
                            }
                            c.setText(txt);
                     }
             }
     });
}
 
public static void main(String[] args)
{
     Application.startApplication(args);
     Form f = new ShowExec();
     f.exitSystemOnClose = true;
     f.show();
     // The application will not exit here.
}
//##################################################################
}
//##################################################################

 

Using newEventThread()/resumeEventThread()

This method in Control is used if you are (or may be) within an Event Thread but wish to wait on some external thread or event without blocking events being delivered to the Window. You would use it like this:

 

// Here I am in an Event handler for the Control c
Object oldEvent = c.newEventThread(c);
try{
     //
     // Now I can block for as long as I need.
     // The Control c will not receive user events, only repaint/resize events.
     //
}finally{
     c.resumeEventThread(oldEvent);
}

 

If the parameter to newEventThread() is null then all Controls will continue to receive events as normal, otherwise the specified control will not receive pen/keyboard events, only repaint and resize events.

 

The method pauseEventThread() is a convenience method that calls newEventThread(this) , i.e. it calls newEventThread() and disables itself.

 

You can safely call newEventThread() even if the current thread is not a Window Event Thread.

Menu and List Events

These events are a little more complicated than the PRESSED and DATA_CHANGED events and need some explaining.

SELECTED and DESELECTED Events

Menu and List Controls generate MenuEvent and ListEvent event objects. In fact ListEvent inherits from MenuEvent and so is very similar, just as List inherits from Menu. These events have a field called selectedItem and this will indicate the item selected (or deselected) that caused the event to be generated. This field is of type Object because it can be either a MenuItem object (which is usually the case) or a String representing the text of the item. So when you are handling a MenuEvent/ListEvent you should always check what type this value is at run-time and not assume it is one or the other.

 

Menu events also have a field called menu. This indicates the Menu or List that the selectedItem belongs to. This may be different from the target of the event, which is the usual way you determine the source of the event. However the target of a MenuEvent may change as the event propagates up chains of sub-menus. Each parent menu that detects a MenuEvent coming from one of its sub-menus will modify the target field so that it appears to come from itself. However, it will not change the menu field, so this always refers to the original generator of the event.  This also happens with the MenuBar control – it too changes the target field so that any events generated by menus that it contains will look as though they come from the MenuBar itself.

PRESSED Events

Menus are generally used for the selection of a single item within a menu or a set of menus. As a result they will also generate a standard PRESSED event when an item (which is not associated with a sub-menu) is selected via the keyboard or pen/mouse press.

 

Lists are generally used differently, and frequently multiple item selection is allowed. Therefore the PRESSED event is generally not generated unless an item is double clicked with the pen/mouse. However a List will generate a DataChangedEvent when the user changes the currently selected item.