Components (visual and not-visual) often fire specific to its business events. For instance text model fires text updated event, button UI component fires fired event every time the button has been pressed, list component fires selected event when a new item in the list has been selected and so on. To handle such sort of events a listener or listeners registration pattern have to be applied. A listener can be registered or removed with calling “on()/off()” methods that are part of zebkit component API:
comp.on([eventName,][path,], callback);
path is an optional parameter to lookup component or components the given event handler have to be registered. Path is X-Path like expression. For instance: "//*"
detects all child components, "//zebkit.ui.Label"
will match all child labels components.
eventName is an optional parameter to say which event exactly has to be caught. For instance: "matrixModel.on('matrixResized', ...);"
. If the parameter has not been specified all available events will be caught.
callback is an event or events handler function.
To stop handling event or events use the following pattern:
comp.off([eventName,][path]);
The parameter meaning is the same to “on(…)” method. If there is no parameters have been specified, all events handlers for the given component will be detached.
To develop a custom component that fires an event or events can be simplified with implementing special “zebkit.EventProducer” interface to add “on()/off()/fire()” methods implementations. Additionally listeners container class has to be declared in “_“ class static variable:
// implement "zebkit.EventProducer" to add event listening and
// firing API in the class
var MyClassThatFiresEvents = zebkit.Class(zebkit.EventProducer, [
function $clazz() {
// define listeners container that keeps two types of events
this._ = zebkit.ListenersClass("propertyAChanged",
"propertyBChanged");
},
function setA(v) {
this.a = v;
// fire appropriate event with API
this.fire("propertyAChanged", [ this, v ]);
},
function setB(v) {
this.b = b;
// fire appropriate event with API
this.fire("propertyBChanged", [ this, v ]);
}
]);
...
var m = new MyClassThatFiresEvents();
m.on("propertyAChanged", function(src, value) {
// handle events
...
});
// this triggers "propertyAChanged" event will be fired
m.setA(10);
UI components generate number of UI specific events:
Pay attention the events types are handled following <h4>overriding pattern</h4> instead of registering listeners via __“on(…)/off(…)” methods call. It is done to save resources, speeds up events handling and minimize possible memory leaks that are probable when developers have to track listeners list.
Overriding pattern means that events catching have to be done by overriding desired UI events handler methods. For instance, if to handle pointer released event do it as follow:
...
var comp = new zebkit.ui.Panel();
...
comp.pointerReleased = function(e) {
// here you will get pointer released event
...
};
or the same with anonymous class paradigm:
...
var comp = new zebkit.ui.Panel([
function pointerReleased(e) {
// here you will get pointer released event
...
}
]);
Pay attention that zebkit UI components can have own implementations of appropriate UI events handlers. In this case don’t forget to call super implementation:
...
var comp = new zebkit.ui.Button("Button", [
function pointerReleased(e) {
// here you will get pointer released event
...
// call super implementation of the UI event handler
this.$super(e);
}
]);
List of available UI events:
UI events names | Description |
---|---|
Pointer events: |
Pointer events that are fired with mouse, touch screen, pen devices. Pointer events handlers get __“zebkit.ui.event.PointerEvent” class instance as its argument. The class has the following fields:
|
Key events: |
Keyboard events. Key event handler methods get “zebkit.ui.event.KeyEvent” class instance as its argument. The class has the following fields:
|
Focus events: |
Focus events. Focus event handler methods get “zebkit.ui.event.FocusEvent” class instance as its argument. The class has the following fields:
|
UI component events: |
UI component events. Component event handler methods get “zebkit.ui.event.CompEvent” class instance as its argument. The class has the following fields:
|
To catch UI events from child components (on all level of UI hierarchy) follow the same overriding appropriate that have been shown before, but in this case add “child” prefix to a name of required handler method name.
For instance, imagine a panel contains number of labels components as its children. To handle pointer pressed events that have occurred over the labels “pointerPressed” method handler can be overriding. It has to be done for every label component. The solution is not handy and generic. Instead add child UI events handler on the level of parent panel component:
Child UI events handling |
zebkit.require("ui", function(ui) {
var root = new ui.zCanvas(400, 300).root;
root.setListLayout(8);
root.setPadding(8);
root.setBorder("plain");
for (var i = 0; i < 5; i++) {
root.add(new ui.Label("Press on " + i + "-th label\n" +
"to change its background")
.setPadding(4))
.setBorder("plain");
}
// catch child labels pointer pressed event to update
// the labels background
root.childPointerPressed = function(e) {
e.source.setBackground(e.source.bg != null ?null:"#44AAFF");
};
});
Zebkit UI is organized as a hierarchy of UI components where every UI component can be a container for other UI components. UI hierarchy is a good developing approach to implement compound UI components where developers can assemble UI components from another UI components. For example “zebkit.ui.Button” component can use any other UI component as its content: images, series of images, combinations of labels and images, etc.
Handling of UI input events (pointer, key, focus) in compound component can be not a trivial thing since the input events are captured with child components. In the case of “zebkit.ui.Button” every time a pointer pressed over its content component (for instance label) the event is sent only to the content component, button itself doesn’t get it. Possible solution is implementing child component UI events handler(s) as it has been described earlier. More graceful solution is follow composite UI component approach.
Composite is an UI component that makes its child components “event transparent”. Event transparency means the child components don’t get any input (pointer, keyboard, etc) events. It looks like the child components transparent for these events. The input events are sent directly to a parent component as if its child component not exist.
To make children of a container events transparent set “catchInput” property in the container to true
value:
Child UI events handling |
zebkit.require("ui", "layout", function(ui, layout) {
var root = new ui.zCanvas("compositeComp1", 400, 250).root;
root.properties({
padding: 8, border: "plain",
layout: new layout.ListLayout(16),
kids : [
new zebkit.ui.Button("Event transparent button"),
new zebkit.ui.Checkbox("Event transparent checkbox"),
new zebkit.ui.TextField("Event transparent field")
]
});
// Make child components of "root" component
root.catchInput = true;
});
More flexible way to add event transparency is defining “catchInput(kid)” method (instead of treating it as a field) that gets a kid component as input parameter. Then the method has to decide whether the given child component has to be make event transparent or not:
Children UI events handling |
zebkit.require("ui", "layout", function(ui, layout) {
var root = new ui.zCanvas("compositeComp2", 400, 300).root;
root.properties({
padding: 8, border: "plain",
layout: new layout.ListLayout(16),
kids : [
new zebkit.ui.Button("Event transparent button"),
new zebkit.ui.Checkbox("Normal check box"),
new zebkit.ui.TextField("Normal field")
]
});
// Make first button of "root" component events transparent.
root.catchInput = function(kid) {
return kid === root.kids[0];
};
});
Zebkit UI events handling and distribution is managed with a special singleton class “zebkit.ui.event.EventManager” class. The manager gets all UI events and then decides how its have to be delivered to destination UI components.
The manager is accessible via “zebkit.ui.events” variable and provides possibility to register global events listeners. Global means the handlers are getting all UI event or events that have happened. To register and unregister global UI events handlers follow the standard pattern:
// listen all pointer pressed events for all actiev UI components
zebkit.ui.events.on("pointerPressed", function(e) {
...
});
For example let’s listening “pointerPressed” event for all UI components to show information window with the source UI components metrics (size and location):
Children UI events handling |
zebkit.require("ui", function(ui) {
var root = new ui.zCanvas("globalEvents", 400, 300).root;
root.setRasterLayout(true);
root.setPadding(8);
root.setBorder("plain");
// add number of components
root.add(new ui.Button("Test Button").setLocation(90,90));
root.add(new ui.Checkbox("Test\nCheck box").setLocation(240,170));
root.add(new ui.TextField("Test text field").setLocation(50,220));
var info = new ui.Tooltip("");
// register global pointer pressed handler
ui.events.on("pointerPressed", function(e) {
if (info.parent !== null) {
info.removeMe();
}
if (root !== e.source) {
// show info about an UI component the pointer pressed
// event has occurred
info.setValue("x = " + e.source.x+",y = "+e.source.y+
"\nw = "+e.source.width+",h = "+e.source.height);
info.toPreferredSize();
info.setLocation(e.absX + 5, e.absY - info.height);
ui.showWindow(root, info);
info.removeMe(1500);
}
});
});