In Buoy, any user event, like clicking on a button, select an item in a list, etc., generates a widget event, which is an object that represents the user's action. There are several kinds of widget events to account for different kinds of user actions (selecting an item, changing a text in an entry, ...). After being generated, a widget event is sent to every object that has registered for that particular kind of event. Any event registration specifies the method that is to be called when the event is sent to the object (rather than through implementing an interface). The immediate benefits from this design are that the code is cleaner and easier to read and maintain than Swing code is and that the responding object does not need to implement a specific interface (and thus a mandatory set of methods, even if some are useless or irrelevant).
There are about thirty different kinds of events at the moment in the Buoy API, but only three of them are of daily use in a usual GUI frame (you probably don't often need to know when the mouse has entered a particular widget...). These usual events are:
(one could argue that the ToolTipEvent class is the fourth most used event if you systematically display tooltips.)
First, how does the program respond to a widget event ? The first thing to do is to ask Buoy to link a specific kind of event for a given widget to the method of an object:
myWidget.addEventLink( EventClass, object, objectMethod
);
Events which are instances of the class EventClass
are
then sent to the method objectMethod of object, if this method accepts an
instance of EventClass
as parameter. It is also valid to
specify a method which does not accept any parameter, in which case the
event is not available during method call.
It is time we setup a demo app to illustrate event use. We'll use six buttons layed out in 3x2 grid container, and a text area to display event messages triggered by selecting the different buttons. The full source code can be downloaded here.
The event links are established in the following code section:
gc.add( button = new BButton( "Button 1" ), 0, 0 );
button.addEventLink( CommandEvent.class, this, "doButton1" );
gc.add( button = new BButton( "Button 2" ), 0, 1 );
button.addEventLink( CommandEvent.class, this, "doButton2" );
gc.add( button3 = new BButton( "Button 3" ), 1, 0 );
button3.addEventLink( CommandEvent.class, this, "doButton3and4" );
gc.add( button4 = new BButton( "Button 4" ), 1, 1 );
button4.addEventLink( CommandEvent.class, this, "doButton3and4" );
gc.add( button = new BButton( "Button 5" ), 2, 0 );
button.addEventLink( CommandEvent.class, this, "doCommand" );
button.setActionCommand( "button5" );
gc.add( button = new BButton( "Button 6" ), 2, 1 );
button.addEventLink( CommandEvent.class, this, "doCommand" );
button.setActionCommand( "button6" );
As can be noticed, sets of buttons 1 and 2, 3 and 4 and 5 and 6 follow
different event schemes. The two first buttons are directly linked to two
respective methods, doButton1
and doButton2
. The
links are established using the following code:
button.addEventLink( CommandEvent.class, this, "doButton1"
);
The first argument to the addEventLink
method is the kind
of event the links considers. The second argument is the object the event
must be linked to. The third argument is the method that is to be called
in case of a relevant event.
The methods
and
doButton1
have no arguments and are directly
called whenever the first (resp. second) button is selected by the user.
The method then prints out a message in the text area :doButton2
public void doButton1()
{
output.append( "Button1 command event\n" );
}
For illustration purposes, we will suppose that these two methods are likely to be called outside the package they belong to, and hence they are declared public. Bear in mind, however, that declaring an event method public or protected is not a Buoy requirement, as we will see shortly.
The buttons 3 and 4 are linked to the same method, which they share :
selecting one of these two buttons triggers a call to
doButton3and4
. Knowing which of the two buttons, 3 or 4,
triggered the event requires access to the event itself. For this reason,
an event instance is specified as method parameter. If there was no need
to know from which button the event originates, the method could be void
of parameter, just as for the two first buttons.
The event source widget is accessed through calling
event.getWidget()
. Comparing this reference to a known widget
makes it possible to track down the widget which triggered the event.
There is however a small inconvenience : the references to the widgets
must be known and hence stored in class variables. Finally, we suppose in
this example that this method might be called from whithin the package or
subclasses and hence is declared protected.
The third scheme is illustrated with the buttons 5 and 6. The action command properties of the buttons are used to identify which button triggered the event, just as with Swing. The advantage over the previous scheme is that there is no need to keep widget references whithin "object scope", but the action command string must be set for each widget, which is tedious for an array of widget.
It is supposed that this last method is not to be called by any other class and hence is declared private. The private scope does not prevent Buoy from calling the method in case of an event, and thus the required level of protection can be applied regardless of the UI event scheme.
All in all, the "direct" scheme, the first one, is the cleanest and easiest to maintain. In some cases, however, one of the two other schemes is the best (or only !) choice : for example, you can link an array of widgets to the same method and identify the event source using scheme #2.
Another important point is that it is not only possible to link the same kind of events for different widgets to one method, but it is also possible to link different kinds of events of the same widget to one method. In fact, the following code applies to any kind of event:
aWidget.addEventLink( Object.class, this, "doWidgetEvent"
);
Of course, sorting out what's happening in doWidgetEvent code is not
very efficient, but it can be interesting to treat
WidgetMouseEvent
s inside a single method, instead of a method
per kind of WidgetMouseEvent
(as stated in the AboutBuoy
document).
Yes, you can use Buoy event handling mechanism for your own use, which might have nothing to do with the graphical interface. Just implement your own class of event and have the relevant classes (i.e. the classes susceptibles of raising events) subclass EventSource . You then add appropriate links using:
anyEventSourceInstance.addEventLink( MyEvent.class, this,
"doMyEvent" );
The event is then triggered by an EventSource instance using the dispatch Event method:
dispatchEvent( myEvent );
import java.awt.Insets; import buoy.event.*; import buoy.widget.*; /** * Event links demo * *@author François Guillet *@created 2004/07/12 */ public class EventDemo extends BFrame { private BButton button3; private BButton button4; private BTextArea output; /** * Constructor for the BButtonDemo object */ public EventDemo() { super( "Event demo" ); BorderContainer bc = new BorderContainer(); GridContainer gc = new GridContainer( 3, 2 ); gc.setDefaultLayout( new LayoutInfo( LayoutInfo.CENTER, LayoutInfo.NONE, new Insets( 3, 3, 3, 3 ), null ) ); BButton button; gc.add( button = new BButton( "Button 1" ), 0, 0 ); button.addEventLink( CommandEvent.class, this, "doButton1" ); gc.add( button = new BButton( "Button 2" ), 0, 1 ); button.addEventLink( CommandEvent.class, this, "doButton2" ); gc.add( button3 = new BButton( "Button 3" ), 1, 0 ); button3.addEventLink( CommandEvent.class, this, "doButton3and4" ); gc.add( button4 = new BButton( "Button 4" ), 1, 1 ); button4.addEventLink( CommandEvent.class, this, "doButton3and4" ); gc.add( button = new BButton( "Button 5" ), 2, 0 ); button.addEventLink( CommandEvent.class, this, "doCommand" ); button.setActionCommand( "button5" ); gc.add( button = new BButton( "Button 6" ), 2, 1 ); button.addEventLink( CommandEvent.class, this, "doCommand" ); button.setActionCommand( "button6" ); bc.add( gc, BorderContainer.CENTER ); bc.add( new BScrollPane( output = new BTextArea( 5, 60 ), BScrollPane.SCROLLBAR_AS_NEEDED, BScrollPane.SCROLLBAR_ALWAYS ), BorderContainer.SOUTH, new LayoutInfo( LayoutInfo.CENTER, LayoutInfo.NONE, new Insets( 5, 5, 5, 5 ), null ) ); setContent( bc ); addEventLink( WindowClosingEvent.class, this, "doQuit" ); pack(); setVisible( true ); } /** * Quit */ private void doQuit() { System.exit( 0 ); } /** * Button1 command event method */ public void doButton1() { output.append( "Button1 command event\n" ); } /** * Button2 command event method */ public void doButton2() { output.append( "Button2 command event\n" ); } /** * Buttons 3 and 4 command events method * */ protected void doButton3and4( CommandEvent ev ) { output.append( "Button3and4 command event: " ); if ( ev.getWidget() == button3 ) output.append( "source widget Button3\n" ); else output.append( "source widget Button4\n" ); } /** * Commands issued by buttons 5 and 6 * */ private void doCommand( CommandEvent ev ) { output.append( "doCommand command event: " ); if ( ev.getActionCommand().equals( "button5" ) ) output.append( "source widget Button5\n" ); else output.append( "source widget Button6\n" ); } /** * Main * *@param args The command line arguments */ public static void main( String[] args ) { new EventDemo(); } }