Showing posts with label composite. Show all posts
Showing posts with label composite. Show all posts

Wednesday, July 22, 2009

DivPanel - Creating a new Widget

Create a composite or extend a widget ?

This question has many answers. In the end it all depends on what is your needs.

When you want to make many widgets work together, like the labeltextbox we created before it's better to create a composite. But when you need to create a new behavior for your widget, maybe it's better to extend a base widget.

As example we will extend the simplePanel, that supports only one widget inside it, to support as many widgets as we need.

Unlike the HorizontalPanel and the VerticalPanel, that uses tables to align the widgets inside it, the simplePanel uses a div to wrap its widget.

Here is the code:

public class DivPanel extends SimplePanel {


private WidgetCollection children = new WidgetCollection(this);


public DivPanel() {

super();

}


public Widget getWidget(int index) {

return getChildren().get(index);

}


public int getWidgetCount() {

return getChildren().size();

}


public int getWidgetIndex(Widget child) {

return getChildren().indexOf(child);

}


public Iterator iterator() {

return getChildren().iterator();

}


public boolean remove(int index) {

return remove(getWidget(index));

}


@Override

public boolean remove(Widget w) {

// Validate.

if (w.getParent() != this) {

return false;

}

// Orphan.

orphan(w);


// Physical detach.

Element elem = w.getElement();

DOM.removeChild(DOM.getParent(elem), elem);


// Logical detach.

getChildren().remove(w);

return true;

}

public void add(Widget child){

add(child,getElement());

}


protected void add(Widget child, Element container) {

// Detach new child.

child.removeFromParent();


// Logical attach.

getChildren().add(child);


// Physical attach.

DOM.appendChild(container, child.getElement());


// Adopt.

adopt(child);

}


protected WidgetCollection getChildren() {

return children;

}


}

First and most important we need to rewrite the add method (the simplePanel.add method verify if you already added a widget and throws an exception).
Second we need to create a WidgetCollection to hold all the widgets that will be accessed.
Let's take a look at the add(Container,widget).
If we use the DOM.appendChild method our page will look like we want, but we need also to call the adopt(widget) method, so the events and handlers already associated with the widget still works.

Also when extending an widget it's very important to know what will be your base class.
It could be the widget class, some Panel class, maybe a TextBox... and so on. Choosing the rigth base widget is a key factor.

Note that when extending the widget class you need also to create a DOM element and call the setElement method. This is the method responsable for binding your element to the page.

Here is another example:

public class DockDiv extends DivPanel{


public final static int NORTH = 0;

public final static int SOUTH = 1;

public final static int EAST = 2;

public final static int WEST = 3;

public final static int CENTER = 4;

private Element north = DOM.createDiv();

private Element south = DOM.createDiv();

private Element east = DOM.createDiv();

private Element west = DOM.createDiv();

private Element center = DOM.createDiv();

private Element row = DOM.createDiv();

private Element container = DOM.createDiv();

public DockDiv(){

container.appendChild(north);

container.appendChild(row);

container.appendChild(south);

row.appendChild(west);

row.appendChild(center);

row.appendChild(east);

getElement().appendChild(container);

east.getStyle().setProperty("float", "left");

west.getStyle().setProperty("float", "left");

center.getStyle().setProperty("float", "left");

}

public void add(Widget child){

add(child,center);

}

public void add(Widget child, int pos){

switch (pos) {

case NORTH:

add(child,north);

break;

case SOUTH:

add(child,south);

break;

case EAST:

add(child,east);

break;

case WEST:

add(child, west);

break;

default:

add(child, center);

break;

}

}

public Element getRegion(int region){

switch (region) {

case NORTH:

return north;

case SOUTH:

return south;

case EAST:

return east;

case WEST:

return west;

default:

return center;

}

}

@Override

public void setWidth(String width) {

super.setWidth(width);

north.getStyle().setProperty("width", width);

row.getStyle().setProperty("width", width);

south.getStyle().setProperty("width", width);

}

public void setCenterSize(String west, String center, String east){

if (west != null && !west.equals("")){

this.west.getStyle().setProperty("width", west);

}

if (center != null && !center.equals("")){

this.center.getStyle().setProperty("width", center);

}

if (east != null && !east.equals("")){

this.east.getStyle().setProperty("width", east);

}

}

}

as you see this DockDiv extends the divPanel (working with divs, not tables).
Also there is some DOM Elements and in the add method we decide what element will be the container of the widget. This shows that you may create as many elements you need.

Friday, July 17, 2009

Composites - When you need to group widgets

When developing an user interface it's quite common to need components more complex than the ones that are available to us. By example the textfield widget does not have a label by default and every time we need to put a text field on a form, we also need to put a label with it.
And if we could create a new widget that was composed of a label and a textfield?
That's the main purpose of the Composite class. The Composite is a widget composed of widgets.
You may create a LabelTextBox composite, that has a label and a textfield. This way you don't need anymore to create each time a label and a textbox (and maybe some panel to layout them). Now you just need to create a LabelTextBox.
Also, using the composite helps you to keep your code clean and organized, by encapsulating all the logic and configuration of those widgets inside the composite class.
Here is an example of a composite:

public class LabelTextField extends Composite {


private HorizontalPanel hPanel;

private Label lblField;

private TextBox txtField;

public LabelTextField(Label lbl, TextBox txt) {

lblField = lbl;

txtField = txt;

hPanel = new HorizontalPanel();

hPanel.add(lblField);

hPanel.add(txtField);

initWidget(hPanel);

}

public String getValue(){

return txtField.getText();

}

public String getLabel(){

return lblField.getText();

}

}

One of the rules to use the composite class is that we must associate another widget as the main widget of this composite, through the method initWidget(Widget w).
In this case the main widget is a HorizontalPanel.
Also note that we hided the TextBox and the Label objects from the external access, and only exposed the getText() methods of the objects.