The Google Web Toolkit works with an event mechanism that implements the Observer pattern: If an event occurs within a certain object called the `observed' object, this triggers a method invocation on each object within a set of `observing' objects called the listeners.
Various event types exist. Most of them (or all of them -- i'm not sure) represent events happening on the user-interface, e.g. a mouse entering or leaving a widget, a mouse click on a widget, etcetera. It was unclear to me how this widget event model worked, and i did not find a good explanation in the docs, so that's why i wrote this post.
The `observed' widget acts as an event source, e.g., a Button acts as a source for click events. In GWT, if a widget acts as an event source it must implement a sourcesXXXEvents interface. There are 15 somewhat sourcesXXXEvents interfaces defined in the GWT API. SourcesClickEvents, SourcesChangeEvents, SourcesKeyboardEvents, etcetera. These interfaces require that event listeners can be added to and removed from the listener collection of the event source.
The `observing' objects -- the ones that are registered as listeners -- must implement methods to deal with events if they are notified. E.g., an object listening to a SourcesKeyboardEvents object must implement the KeyBoardListener interface, requiring it to implement the onKeyDown, onKeyPress, onKeyUp methods. As such, each listener interface requires its own methods to be implemented. This is all pretty standard and straightforward if you ever did some event programming in something like Swing or Windows.
So-far-so-good, but one question remains: what is the rock-bottom source of events in GWT? Is declaring the implementation of an SourcesXXXEvents interface enough to have the events actually generated?
For instance, the Button class does not implement the SourcesMouseEvents interface. (At least not in GWT 1.5.3.) The SourcesMouseEvents interface contains methods onMouseEnter() and onMouseLeave(). These could be used to e.g. change the widget's text when the mouse hovers over it. Suppose i subclass Button as follows:
public class MouseEventsButton extends Button implements SourcesMouseEvents {
MouseListenerCollection mouseListenerCollection;
public MouseEventsButton(String string) {
super(string);
}
public void addMouseListener(MouseListener listener) {
if (mouseListenerCollection == null) {
mouseListenerCollection = new MouseListenerCollection();
}
mouseListenerCollection.add(listener);
}
public void removeMouseListener(MouseListener listener) {
if (mouseListenerCollection != null) {
mouseListenerCollection.remove(listener);
}
}
}
Will my new button respond to a mouse entering and leaving it now?? Let's try.
NOOOOO, that doesn't work... Nothing happens if i move my mouse over the MouseEventsButton.
public class DemoGwtEvents implements EntryPoint {
public void onModuleLoad() {
VerticalPanel vPanel = new VerticalPanel();
vPanel.setWidth("100%");
vPanel.setHorizontalAlignment(VerticalPanel.ALIGN_CENTER);
RootPanel.get().add(vPanel);
// create the new button
MouseEventsButton mouseEventsButton = new MouseEventsButton("Rub me!");
// add a listener to it that changes text on events
mouseEventsButton.addMouseListener(new MouseListenerAdapter() {
public void onMouseEnter(Widget sender) {
Button b = (Button) sender;
b.setHTML("thank you");
}
public void onMouseLeave(Widget sender) {
Button b = (Button) sender;
b.setHTML("rub me again!!");
}
});
vPanel.add(mouseEventsButton);
}
}
The key is that i must make a connection between low-level DOM-events and high-level GWT events somehow. DOM-events are events that happen in the browser window. GWT events happen in the Java layer of a GWT app. Somehow, the DOM-events must be captured and re-launched into the Java layer. This is done in two steps:
- Tell the new widget (the button) to CAPTURE certain DOM-events by calling sinkEvents. In our case this is
this.sinkEvents(Event.ONMOUSEOVER | Event.ONMOUSEOUT); - Write an event handler to capture the sunk DOM events and re-launch them in the java world. This is done by overriding the onBrowserEvent method of the widget. In our case this is
public void onBrowserEvent(Event event) {Note that to throw the events into the java world, the MouseListenerCollection class provides utility methods as `fireMouseLeave' etc.
super.onBrowserEvent(event);
// Handle events as a normal Button would
int type = DOM.eventGetType(event);
// Look at the type of event again
switch (type) {
case Event.ONMOUSEOVER:
mouseListenerCollection.fireMouseEnter(this);
break;
case Event.ONMOUSEOUT:
mouseListenerCollection.fireMouseLeave(this);
}
}
So, in summary we may say that GWT widget events are created explicitly as 2-nd layer events after explicitly catching and handling low-level DOM events.
public class MouseEventsButton extends Button implements SourcesMouseEvents {
MouseListenerCollection mouseListenerCollection;
public MouseEventsButton(String string) {
super(string);
this.sinkEvents(Event.ONMOUSEOVER | Event.ONMOUSEOUT);
}
public void addMouseListener(MouseListener listener) {
if (mouseListenerCollection == null) {
mouseListenerCollection = new MouseListenerCollection();
}
mouseListenerCollection.add(listener);
}
public void removeMouseListener(MouseListener listener) {
if (mouseListenerCollection != null) {
mouseListenerCollection.remove(listener);
}
}
public void onBrowserEvent(Event event) {
super.onBrowserEvent(event);
// Handle events as a normal Button would
int type = DOM.eventGetType(event);
// Look at the type of event again
switch (type) {
case Event.ONMOUSEOVER:
mouseListenerCollection.fireMouseEnter(this);
break;
case Event.ONMOUSEOUT:
mouseListenerCollection.fireMouseLeave(this);
}
}
}
Looking at the current version of the source code (dec. 8, 2008) checked out from svn, many of the classes and methods involved in event handling have been deprecated, so it looks asif a major refactoring of the event mechanism is pending. Hopefully, this will be better documented than it was in the current version.
Geen opmerkingen:
Een reactie posten