PDA

View Full Version : FormPanel with widgets on same line support



pochemuto
19 Mar 2012, 6:02 AM
I'm try to extend FormLayout to make possible to put widgets on same line in vertical mode. General idea: mix the FormLayout and the MultiField. Also, I add new LayoutData with fields sameRow for put widget at last row and fixedSize.
Layout overrides method renderComponent. This method adds a component to buffer (List<Component>) when it or next widget is marked as same row widget (has layout data with sameRow=true). When line ends, layout renders row like it does MultiField (gets all components from buffer, puts them to HorizontalPanel, adds labels if need)

So, I have next problems.
When component added to HorizontalPanel it gets out from container and breaks renderAll method in layout - there is iteration over container children, but container items count changed in renderComponent.

Maybe has another way to add more than one fields to one row in FormLayout?


import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;


import com.extjs.gxt.ui.client.GXT;
import com.extjs.gxt.ui.client.Style.Orientation;
import com.extjs.gxt.ui.client.core.El;
import com.extjs.gxt.ui.client.core.Template;
import com.extjs.gxt.ui.client.event.ComponentEvent;
import com.extjs.gxt.ui.client.util.Params;
import com.extjs.gxt.ui.client.widget.BoxComponent;
import com.extjs.gxt.ui.client.widget.Component;
import com.extjs.gxt.ui.client.widget.ComponentHelper;
import com.extjs.gxt.ui.client.widget.Container;
import com.extjs.gxt.ui.client.widget.HorizontalPanel;
import com.extjs.gxt.ui.client.widget.Label;
import com.extjs.gxt.ui.client.widget.LayoutContainer;
import com.extjs.gxt.ui.client.widget.VerticalPanel;
import com.extjs.gxt.ui.client.widget.form.Field;
import com.extjs.gxt.ui.client.widget.form.HiddenField;
import com.extjs.gxt.ui.client.widget.form.FormPanel.LabelAlign;
import com.extjs.gxt.ui.client.widget.layout.FormLayout;
import com.extjs.gxt.ui.client.widget.layout.LayoutData;
import com.extjs.gxt.ui.client.widget.layout.TableData;
import com.google.gwt.user.client.DOM;


public class MultiFormLayout extends FormLayout {
private List<Component> sameRowBuffer = new ArrayList<Component>();
private List<LayoutContainer> multiRows = new ArrayList<LayoutContainer>();
private Orientation orientation = Orientation.HORIZONTAL;
private int spacing = 6;
private int totalChildren = 0;
private String labelStyle;
private String elementStyle;
private Template fieldTemplate;
private final static Logger logger = Logger.getLogger("MultiFormLayout");

@Override
protected void onAdd(Component component) {
super.onAdd(component);
}

@Override
protected void renderAll(Container<?> container, El target) {
totalChildren = container.getItemCount();
super.renderAll(container, target);
}

@Override
protected void renderComponent(Component component, int index, El target) {
boolean last = totalChildren == index + 1;

if (markedInSameRow(component)) {

sameRowBuffer.add(component);

if (last) {
renderRow(target, index);
}

} else {
Component nextComponent = null;
if (!last) {
nextComponent = container.getItem(index + 1);
}

if (!sameRowBuffer.isEmpty()) {
renderRow(target, index);
}

if (last) {
super.renderComponent(component, index, target);
} else {

if (markedInSameRow(nextComponent)) {
sameRowBuffer.add(component);
} else {
super.renderComponent(component, index, target);
}
}


}
}


private boolean markedInSameRow(Component c) {
LayoutData layoutData = getLayoutData(c);
return layoutData != null && layoutData instanceof MultiFormData && ((MultiFormData) layoutData).isSameRow();
}

@SuppressWarnings("rawtypes")
private void renderRow(El target, int index) {
boolean vertical = orientation == Orientation.VERTICAL;
LayoutContainer lc;
if (vertical) {
lc = new VerticalPanel();
} else {
lc = new HorizontalPanel();
}


if (GXT.isIE) {
lc.setStyleAttribute("position", "relative");
}


Field<?> firstField = null;
for (int i = 0, len = sameRowBuffer.size(); i < len; i++) {
Component f = sameRowBuffer.get(i);


boolean last = i == (sameRowBuffer.size() - 1);

LayoutData layoutData = ComponentHelper.getLayoutData(f);

TableData data = null;
if (layoutData != null && layoutData instanceof TableData) {
data = (TableData) layoutData;
} else {
data = new TableData();
}
String style = "position: static;";


if (vertical && !last && spacing > 0) {
style += "paddingBottom:" + spacing + "px;";
} else if (!vertical && !last && spacing > 0) {
style += "paddingRight:" + spacing + "px;";
}

if (f instanceof Field) {
Field<?> field = (Field<?>) f;
String label = field.getFieldLabel();

if (!getHideLabels() && !field.isHideLabel() && label != null && !label.isEmpty() && i > 0) {
String ls = field.getLabelSeparator() != null ? field.getLabelSeparator() : getLabelSeparator();
field.setLabelSeparator(ls);

MultiFormData labelData = new MultiFormData(true, true);
labelData.setStyle("position: static; paddingRight:" + spacing + "px;");
labelData.setStyleName("x-form-label x-component");
Label labelElement = new Label(label + ls);
labelElement.render(DOM.createDiv());
labelElement.el().setElementAttribute("for", field.getId() + "-input");
labelElement.setStyleAttribute("white-space", "nowrap");
lc.add(labelElement, labelData);
}

if (firstField == null) {
firstField = field;
}
}

data.setStyle(style);
lc.add(f, data);
}
if (firstField == null) {
firstField = new HiddenField();
}

String ls = firstField.getLabelSeparator() != null ? firstField.getLabelSeparator() : getLabelSeparator();
firstField.setLabelSeparator(ls);
Params p = new Params();


if (getHideLabels()) {
firstField.setHideLabel(true);
}


p.add(firstField.getId());
p.add(firstField.getFieldLabel());
p.add(labelStyle);
p.add(elementStyle);
p.add(ls);
p.add(firstField.isHideLabel() ? "x-hide-label" : "");
p.add("x-form-clear-left");
p.add(firstField.getLabelStyle());


String inputId = firstField.getId();
p.add(inputId);


fieldTemplate.insert(target.dom, index, p);

if (lc.isRendered()) {
target.selectNode(".x-form-el-" + firstField.getId()).appendChild(lc.getElement());
} else {
lc.render(target.selectNode(".x-form-el-" + firstField.getId()).dom);
}
if (firstField.getStyleName().contains("-wrap")) {
inputId += "-input";
target.selectNode(".x-form-el-" + firstField.getId()).previousSibling().setAttribute("for", inputId);
}


multiRows.add(lc);
sameRowBuffer.clear();
}

@Override
protected void onLayout(Container<?> container, El target) {
if (getHideLabels()) {
labelStyle = "display:none";
elementStyle = "padding-left:0;";
} else {
int labelPad = getLabelPad();
int labelWidth = getLabelWidth();
LabelAlign labelAlign = getLabelAlign();
int pad = labelPad != 0 ? labelPad : 5;
labelStyle = "width:" + (labelWidth) + "px";
elementStyle = "padding-left:" + (labelWidth + pad) + "px";
if (labelAlign == LabelAlign.TOP) {
labelStyle = "width:auto;";
elementStyle = "padding-left:0;";
}
}

if (fieldTemplate == null) {
StringBuffer sb = new StringBuffer();
sb.append("<div role='presentation' class='x-form-item {5}' tabIndex='-1'>");
sb.append("<label for={8} style='{2};{7}' class=x-form-item-label>{1}{4}</label>");
sb.append("<div role='presentation' class='x-form-element x-form-el-{0}' id='x-form-el-{0}' style='{3}'>");
sb.append("</div><div class='{6}' role='presentation'></div>");
sb.append("</div>");
fieldTemplate = new Template(sb.toString());
fieldTemplate.compile();
}

super.onLayout(container, target);
}

@Override
protected void onResize(ComponentEvent ce) {
super.onResize(ce);
}

@Override
public void layout() {
super.layout();

for (LayoutContainer container : multiRows) {
int childsCount = container.getItemCount();
int width = container.el().getParent().getWidth(true);
if (!GXT.isBorderBox) {
width -= ((childsCount - 1) * spacing);
}

if (orientation == Orientation.HORIZONTAL) {


int fixedSize = 0;
int fixedCount = 0;
// вычисляем ширину виджетов исключая фиксированные
for (int i = 0; i < childsCount; i++) {
Component component = container.getItem(i);
LayoutData layoutData = getLayoutData(component);

if (layoutData instanceof MultiFormData && ((MultiFormData) layoutData).isFixedSize()) { // фиксированный размер
fixedCount++;
if (component instanceof BoxComponent) {
fixedSize += ((BoxComponent) component).getWidth();
}
}
}
logger.info("fixed: " + fixedCount + " width: " + fixedSize);
int w = (width - fixedSize) / (childsCount - fixedCount);
changeRowChildrenWidth(container, w);

} else {
changeRowChildrenWidth(container, width);
}
}

}

private void changeRowChildrenWidth(LayoutContainer container, int width) {
int childsCount = container.getItemCount();
for (int i = 0; i < childsCount; i++) {
Component component = container.getItem(i);

LayoutData layoutData = getLayoutData(component);
if (layoutData instanceof MultiFormData && ((MultiFormData) layoutData).isFixedSize()) {
continue;
}

if (component instanceof BoxComponent) {
((BoxComponent) component).setWidth(width);
}
}
}
}


import com.extjs.gxt.ui.client.widget.layout.TableData;

public class MultiFormData extends TableData {
private boolean sameRow;
private boolean fixedSize;


public MultiFormData() {
super();
}

public MultiFormData(boolean sameRow) {
this.sameRow = sameRow;
}

public MultiFormData(boolean sameRow, boolean fixedSize) {
this(sameRow);
this.fixedSize = fixedSize;
}

public boolean isSameRow() {
return sameRow;
}


public void setSameRow(boolean sameRow) {
this.sameRow = sameRow;
}


public boolean isFixedSize() {
return fixedSize;
}


public void setFixedSize(boolean fixedSize) {
this.fixedSize = fixedSize;
}
}

harun
19 Mar 2012, 7:25 AM
Did you try this:



MultiField mf= new MultiField();
mf.setFieldLabel("your label");
mf.setSpacing(20);


mf.add( new TextField<String>() );

mf.add( new TextField<String>() );

formPanel.add(mf);

pochemuto
19 Mar 2012, 7:31 AM
I tried, but then I can't bind to TextField, because MultiField now wraps both fields. In this case mf it's single Field with own value

harun
19 Mar 2012, 7:35 AM
If I understand you right,

You don't need to bind textfield, you can easily add button or any widget to multifiled.

I reccommend you that paste my code your panel and see result!

pochemuto
19 Mar 2012, 7:45 AM
Yes, it's look exactly how I need. But I have bean with properties firstName ans lastName for example and I want to bind it to FormPanel with two fields with names firstNameField and lastNameFiels which placed at same line. So, when they are placed into MultiField with name "names", forms has one property "names" instead two "firstNameField" and "lastNameFiels" and I can't bind my bean to field ((

harun
19 Mar 2012, 8:07 AM
If I were you, I add bind listener to formbindings and when bind changed, I manually set firstName value and lastName value.

maybe following code helps you:


formBinding.addListener( Events.Bind, new Listener<BaseEvent>() {

@Override
public void handleEvent( BaseEvent be)
{
tfFirstName.setValue( "Harun's Solution" );
tfLastName.setValue( "is the best solution" );

}
} );