CustomField setWidth bug?

I’m using the Vaadin add-on CustomField to create a new field called UnitsField. Basically I want a field that will display some sort of numerical units indicator in a label on the right side of the field. For instance, if I had a BitRate field, I want “kbps” to appear on the right side of the field.

I subclassed CustomField and created a HorizontalLayout in the constructor, containing a TextField and a Label, following the BooleanField example. I modified the first Form example from the Vaadin sampler to use my new custom field. When I tried to set the width of the field for the custom field using COMMON_FIELD_WIDTH (as is done for the text fields in the example), the right side of the text field is cut off, and the units label is not visible. Any standard field (e.g. text field) responds appropriately to setting the width, but my custom field does not.

I also tried using BooleanField and CityField in the form example, and the same problem exists. I am now thinking that any CustomField subclass will not respond appropriately to setWidth(). If the width is set to anything less than whatever default it’s using, then the custom field is cut off on the side.

Have I found a bug in this add-on?

Thanks,
David

CustomField does not have any special layout/rendering logic - it is actually a pure server side add-on using CustomComponent.

I suspect you have a problem with an illegal combination of relative sizes and components of undefined size somewhere. Try to add “?debug” to your URL, click “Analyze Layouts” and see if it reports problems. Also, maybe your HorizontalLayout or its contents are not configured to expand the text field (use e.g. width 100% for the layout, expand ratio 1 and width 100% for the TextField and width undefined (-1px) for the Label).

If that does not help, try to create a minimal example exhibiting the issue, e.g. just using CustomComponent, HorizontalLayout, TextField and Label.

Thanks for you reply, Henri. I have tried a variety of approaches such as those. I don’t think it’s necessarily a problem with my widget, because I observed the same problem using the sample custom fields included with the CustomField add-on. I will paste my code here to illustrate.

import java.util.Arrays;

import org.vaadin.addon.customfield.demo.data.City;
import org.vaadin.addon.customfield.demo.field.CityField;

import com.vaadin.data.Item;
import com.vaadin.data.util.BeanItem;
import com.vaadin.ui.Component;
import com.vaadin.ui.DefaultFieldFactory;
import com.vaadin.ui.Field;
import com.vaadin.ui.Form;
import com.vaadin.ui.TextField;
import com.vaadin.ui.VerticalLayout;

@SuppressWarnings("serial")
public class UnitsFieldTest extends VerticalLayout {
    // the 'POJO' we're editing
    AudioTrack song;

    public UnitsFieldTest() {
    	song = new AudioTrack();
    	BeanItem<AudioTrack> songItem = new BeanItem<AudioTrack>(song);
        setSpacing(true);
        
		// Create the Form
		final Form songForm = new Form();
		songForm.setCaption("Audio track details");
		songForm.setWriteThrough(false); // we want explicit 'apply'
		songForm.setInvalidCommitted(false); // no invalid values in datamodel
		
		// FieldFactory for customizing the fields and adding validators
		songForm.setFormFieldFactory(new SongFieldFactory());
		songForm.setItemDataSource(songItem); // bind to POJO via BeanItem
		
		// Determines which properties are shown, and in which order:
		songForm.setVisibleItemProperties(Arrays.asList(new String[] {
				"name", "artist", "placeOfRecording" }));
		// Add form to layout
        addComponent(songForm);
        
    }

}

class SongFieldFactory extends DefaultFieldFactory {

	private static final long serialVersionUID = 1049955366709543836L;
	private static final String COMMON_FIELD_WIDTH = "8em";

	public SongFieldFactory() {
    }

    @Override
    public Field createField(Item item, Object propertyId,
            Component uiContext) {
        Field f;
        if ("placeOfRecording".equals(propertyId))  {
            f = createUnitsField(propertyId);
        } else {
            f = super.createField(item, propertyId, uiContext);
        }

        if ("name".equals(propertyId)) {
            TextField tf = (TextField) f;
            tf.setRequired(true);
            tf.setRequiredError("Please enter the track name");
            tf.setWidth(COMMON_FIELD_WIDTH);
        } else if ("artist".equals(propertyId)) {
            TextField tf = (TextField) f;
            tf.setRequired(true);
            tf.setRequiredError("Please enter the artist name");
            tf.setWidth(COMMON_FIELD_WIDTH);
        } 

        return f;
    }

    private Field createUnitsField(Object propertyId) {
    	CityField cf = new CityField();
    	cf.setCaption("City");
    	cf.setWidth(COMMON_FIELD_WIDTH);
        return cf;
    }
}


class AudioTrack {
	private static final long serialVersionUID = 1049955366709543835L;
	public String name;
	public String artist;
	public int bitRate;
	public boolean hasDrm;
	public City placeOfRecording;

	public boolean isHasDrm() {
		return hasDrm;
	}

	public void setHasDrm(boolean hasDrm) {
		this.hasDrm = hasDrm;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getArtist() {
		return artist;
	}

	public void setArtist(String artist) {
		this.artist = artist;
	}

	public int getBitRate() {
		return bitRate;
	}

	public void setBitRate(int bitRate) {
		this.bitRate = bitRate;
	}

	public City getPlaceOfRecording() {
		return placeOfRecording;
	}

	public void setPlaceOfRecording(City placeOfRecording) {
		this.placeOfRecording = placeOfRecording;
	}
}

With each of the widgets being set to a width of 8em, the two TextFields responded appropriately, but the custom field CityField did not resize its contained combo box accordingly. Instead, the right side is cut off. Here’s a screen shot:

I also got the same results when I used the example BooleanField to represent the hasDrm field, and I used a setWidth argument small enough to correctly resize the text fields, but truncate the button.

I ran “analyze layouts” on the above example, and got these results:

Layouts analyzed on server, total top level problems: 0 

Note that the component error shown in the screenshot is unrelated to the field being cut off; it’s probably just something with how I am setting up the model in the example. I am observing the same field truncation behavior in my application with my custom field, without that component error.

[color=#ff0000]
Caused by: java.lang.IllegalAccessException: Class com.vaadin.data.util.MethodProperty can not access a member of class com.example.vaadintest2.AudioTrack with modifiers "public"
	at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:65)
	at java.lang.reflect.Method.invoke(Method.java:588)
	at com.vaadin.data.util.MethodProperty.getValue(MethodProperty.java:610)
[/color]

OK, I went back and played with my horizontal layout some more, and actually got it working correctly. I am surprised since the example fields don’t seem to work well, but maybe that is just a problem with how the example fields are laid out and not the CustomField code itself. I think the key change was that I called setSizeFull() on the layout. I am wondering if you add layout.setSizeFull() as the next to last line of the CityField and BooleanField constructors if that will cause things to work better.

If you want the field contents to adapt to the size of the surrounding layout, you should use width 100% on both the layout and the CustomField itself. On the other hand, if you want the form or other location to reserve space for the field based on the “natural size” of the field, those widths should be undefined.

Maybe some of the examples could be improved anyway, or a convenience setWidth() added on some of them that would also update the size of the contained layout/component.