Implementing a Custom Browser Area
A browser area is one of the most vital parts of a cockpit perspective. It's responsible for displaying the perspective's main content and managing browsers. Moreover, searching for, displaying, as well as activating items is all done here. Understand how browser areas work and learn how you can create your own browser area.
The Easy Way - Using the Default Browser Area
The cockpit extension comes with a powerful default browser area implementation. The default browser area offers standard browser handling functionality such as minimizing, restoring, closing and creating new browsers. Moreover, together with the standard browser components, functionality for searching for and displaying all kinds of items is already provided.
In order to use the default browser area, you need to to specify your browser area's main type in Spring. A browser area's main type is the type for which it is primarily responsible. For example the browser area and the browsers in a product perspective typically offers the means for searching for and displaying all kinds of products, thus the main type of the browser area would be the Product.
Example
The following code snippet shows how to configure a browser area with its main type set to Product:
<bean id="MyPerspective" class="mypackage.MyPerspective" scope="session" parent="BasePerspective"> ... <property name="browserArea"> <bean id="MyBrowserArea" parent="BaseSearchBrowserArea"> <property name="rootSearchTypeCode" value="Product"/> </bean> </property> ... </bean>
The important part is here:
<property name="rootSearchTypeCode" value="Product"/>
where you specify the type code of the area's main type.
The Result
The result can be seen in the pictures below

Creating Your Own Custom Browser Area
This section guides you through the steps required to create your own customized browser area.
Browser Area Logic and Layout
A browser area is basically made up of two things: a Java class and a ZUL file.
The first one typically contains all the logic, whereas the latter defines the layout of the area. However, in some cases it is useful to have some simple logic in the ZUL file as well. An example of this can be seen in the Custom Layout section below.
Custom Layout
The cockpit extension is shipped with a standard browser area ZUL file, baseSearchBrowserArea.zul which can be used as template when writing your own customized area. An example of its layout can be seen in the picture below.
The construction of a layout is simple. At the top, there is a panel with a search field and a search button is shown. After entering a search string to the search field, a user can either click the search button or press ENTER in order to open up a new browser window.
The ZUL file holds the logic for opening up the new browser and the following snippet illustrates how it can be done.
...
<toolbarbutton label="Search" ...>
<attribute name="onClick">
// create new browser
MyBrowserModel browserModel = new MyBrowserModel();
// get the browser area
UIBrowserArea area = UICockpitSession.getCurrentPerspective().getBrowserArea();
// add the browser at position 0 to the browser area
if(area.addVisibleBrowser(0,browserModel))
{
// update browser area
UICockpitSession.getCurrentPerspective().getBrowserArea().update();
}
// give the new browser focus
browserModel.focus();
</attribute>
</toolbarbutton>
...
In the center area the browser area's main content is shown. This area typically contains one or more browsers, depending on the mode used. In this particular example, a search browser is shown, which allows the user to search for and display items.
Getting Started - Writing Your Own Custom Browser Area ZUL File
You do not have to use the baseSearchBrowserArea.zul as a base when creating your own custom browser area.
You can write your own browser area .zul file from scratch. It requires basic ZK knowledge.
If you plan to use the AbstractBrowserArea class or any of its subclasses, you need to consider few requirements:
- The root component in your ZUL file should give the browser area focus when clicked, that is it needs to support click event handling
- The browser area requires a main content component of type <borderlayout> without any children to be set
These requirements need to be met in order for the browser area to work correctly.
Create your very own ZUL file, customBrowserArea.zul which illustrates how this can be done
customBrowserArea.zul
<?taglib uri="http://www.zkoss.org/dsp/web/core" prefix="c"?>
<div width="100%" height="100%" onClick="UICockpitSession.getCurrentPerspective().getBrowserArea().setFocus(true)" action="onclick: comm.sendClick(#{self},null)">
<borderlayout width="100%" height="100%" onCreate="UICockpitSession.getCurrentPerspective().getBrowserArea().initBrowsers(self)"/>
</div>
The call needs some further explanation:
UICockpitSession.getCurrentPerspective().getBrowserArea().initBrowsers(self)
This line is responsible for setting the area's main content component. As the second requirement above states, this component needs to be of type <borderlayout>. The area's main content component is the container used by the area to hold and display browsers. It is the browser area that controls what kind and how many of browsers it contains. As the name indicates, once the main content component has been set, the area is automatically initialized by calls to its methods initialize() and update() - in that order. The initialize() is discussed further in the Custom Logic - Start from Scratch or Extend an Existing Class section below.
Your custom ZUL file, customBrowserArea.zul does not contain anything but a main content component, so add a label and a button.
<div width="100%" height="100%" onClick="UICockpitSession.getCurrentPerspective().getBrowserArea().setFocus(true)" action="onclick: comm.sendClick(#{self},null)">
<label value="My customized browser area"/>
<borderlayout width="100%" height="80%" onCreate="UICockpitSession.getCurrentPerspective().getBrowserArea().initBrowsers(self)"/>
<toolbarbutton label="What's this?">
<attribute name="onClick">
Messagebox.show("It's an amazingly pimped browser area!");
</attribute>
</toolbarbutton>
</div>
The result looks like this:

The custom layout example provided here shows how you can customize the layout of your browser area and add some simple logic by writing your own ZUL file. In this case showing a pop-up dialog box once the What's this? button is clicked.
Custom Logic - Start from Scratch or Extend an Existing Class
Every browser area used in a base cockpit application needs to implement the UIBrowserArea interface.
The interface itself contains more than thirty methods and implementing your own browser area from scratch can be quite time-consuming. Instead, the preferred way to go is to extend one of the existing browser areas. In this tutorial you extend the most general class implementing the UIBrowserArea interface, namely AbstractBrowserArea.
Getting Started - Creating the Class CustomBrowserArea.java
Create a class which extends the abstract class AbstractBrowserArea. AbstractBrowserArea provides default implementations for most of the methods of the UIBrowserArea interface. However, there are still a few abstract methods which need to be implemented.
The following snippet shows the code so far:
CustomBrowserArea.java
public class CustomBrowserArea extends AbstractBrowserArea
{
@Override
public BrowserModelListener getBrowserListener()
{
return null;
}
@Override
public void initialize()
{
}
@Override
public void saveQuery(final BrowserModel browserModel)
{
}
}
As you can see above, you need to provide the implementations for three methods:
- getBrowserListener - It should return the model listener to be used by the area for handling browser events.
- initialize - It is called when the browser area is created. Typically creates and displays a browser.
- saveQuery - It is called when a query should be saved. Typically passes the event on to any registered BrowserAreaListeners.
Implementing the initialize() Method
As mentioned above, the initialize() method typically creates and displays the area's initial browser. This procedure can be divided into the following steps:
- Create browser model.
- Add the created browser to the area's visible browsers.
- Give the browser focus.
- Update the browser's items, that is its content.
In this example you add a browser of type DefaultSearchBrowserModel, which is the same kind of browser which is used by the default browser area. The DefaultSearchBrowserModel class implements the SearchBrowserModel interface, and offers not only the functionality for displaying items in different fashions, but also the simple search, advanced search dialogs,and item activation.
This browser requires a main type of type ObjectTemplate to be passed as constructor parameter.
Creating a new instance with main type set to Item looks like this:
BrowserModel browserModel = new DefaultSearchBrowserModel(UISessionUtils.getCurrentSession().getTypeService().getObjectTemplate("Item"));
When you created the browser, you need to tell the area to show it, so you add it to the area's visible browsers:
this.addVisibleBrowser(browserModel);
Finally give our browser focus and make sure its content is updated:
this.setFocusedBrowser(browserModel); browserModel.updateItems();
The complete initialize method now looks like this with some minor things added:
@Override
public void initialize()
{
BrowserModel browserModel = null;
if(this.browsers == null || this.browsers.isEmpty())
{
browserModel = new DefaultSearchBrowserModel( UISessionUtils.getCurrentSession().getTypeService().getObjectTemplate("Item") );
}
else
{
browserModel = this.browsers.get(0);
}
this.addVisibleBrowser(browserModel);
this.setFocusedBrowser(browserModel);
browserModel.updateItems();
}
Implementing the getBrowserListener() Method
In order for a browser area to be notified of and react on browser events it needs to register a BrowserModelListener with the browsers it is managing. Depending on what kind of browsers your browser area support, the getBrowserListener() method needs to return an adequate browser listener. The following UML diagram shows the browser model hierarchy. The required corresponding browser model listener is shown beside each browser model.
Your custom browser area supports search browsers, so the listener needs to implement the SearchBrowserModelListener interface.
You use the DefaultSearchContextBrowserModelListener shipped with the cockpit framework. Writing your own browser model listener isn't a subject of this article, but the users who wish to write their own browser model listeners, this class can serve as a good starting point.
Creating and returning a DefaultSearchContextBrowserModelListener is easy. You need to pass the browser area as constructor parameter. This is important, since the listener needs to be able to access the browser area, for example in order to minimize a browser. To see how the method is implemented, the following The Complete Class CustomBrowserArea.java section.
Implementing the saveQuery(final BrowserModel browserModel) Method
If you want to be able to save queries and do not need to provide some custom behavior when doing so this method only needs to notify the registered BrowserAreaListeners that a query should be saved. You need to use the following line of code:
@Override
public void saveQuery(final BrowserModel browserModel)
{
this.fireBrowserQuerySaved(browserModel)
}
The Complete Class CustomBrowserArea.java
The complete code looks like this:
CustomBrowserArea.java
public class CustomBrowserArea extends AbstractBrowserArea
{
private boolean initialized = false;
private BrowserModelListener browserListener = null;
@Override
public BrowserModelListener getBrowserListener()
{
// always return the same listener
if(this.browserListener == null)
{
// create a new browser model listener and pass this browser area as parameter
this.browserListener = new DefaultSearchContextBrowserModelListener(this);
}
return this.browserListener;
}
@Override
public void initialize()
{
// only initialize once
if(!this.initialized)
{
BrowserModel browserModel = null;
if(this.browsers == null || this.browsers.isEmpty())
{
// area has no browsers, so create a new one
browserModel = new DefaultSearchBrowserModel( UISessionUtils.getCurrentSession().getTypeService().getObjectTemplate("Item") );
}
else
{
// area already has a browser, so use the first one
browserModel = this.browsers.get(0);
}
// show browser
this.addVisibleBrowser(browserModel);
// give browser focus
this.setFocusedBrowser(browserModel);
// make sure browser is updated
browserModel.updateItems();
}
}
@Override
public void saveQuery(final BrowserModel browserModel)
{
// pass on the event to any registered browser area listeners
this.fireBrowserQuerySaved(browserModel);
}
}
BrowserAreaListeners - Passing on Events to the Perspective
Not all events can be completely handled by the browser areas themselves. Some actions might also affect other areas, thus implying that the event is passed on to the perspective somehow.
For example when a browser is focused, opened, minimized or closed, the navigation area's open browser section needs to be updated in order to reflect the new state. Below is an example how it looks when a new browser is opened.

For this purpose, BrowserAreaListeners are used. Normally the perspective registers a BrowserAreaListener with its browser area once the area is created. Some of the BrowserAreaListener methods are listed below.
- void itemActivated(final TypedObject activeItem) - Called when an item is activated (opened).
- void browserMinimized(final BrowserModel browserModel) - Called when a browser is minimized.
- void browserFocused(final BrowserModel browserModel) - Called when a browser is focused.
Notifying all registered BrowserAreaListeners
When extending the AbstractBrowserArea all methods needed in order to notify the browser area listeners are already provided. Their names typically correspond to the names of the methods in the BrowserAreaListener interface, with the added prefix "fire". Let's take item activation as an example.
When an item is activated in a browser, the corresponding browser notifies its registered BrowserModelListeners. In your case it is the listener returned by your browser area's getBrowserListener() method, by calling their itemActivated(final TypedObject item) method. Now, typically the area does not need to do anything itself, but merely acts as a proxy, forwarding the event to the perspective. This is done by calling fireItemActivated(final TypedObject item).
To sum it up, the flow looks like this:
- Item is activated in browser.
- For each registered browser model listener, the browser calls the itemActivated(final TypedObject item) method.
- The browser model listener passes the event on to the registered browser area listeners e.g. perspective by calling fireItemActivated(item).