Multicolumn Panel with FormLayout

I solved my layout needs implementing a custom panel.

I will post the code: any comment on that is appreciated. I hope this can be useful to someone.

The component ia a panel that maintains a list of columns (each column ia a FormLayout). The component also manage automatically the tabIndex order to have it horizzontal.


public class DetailPanel extends Panel {
	
	private static final long serialVersionUID = -4703606467813607326L;
	
	private ArrayList<CustomTabIndexFormLayout> columns = new ArrayList<CustomTabIndexFormLayout>();
	private int requestedColumns = 0;
	private int firstTabIndex = 0;
	
	public DetailPanel(String caption, String width, int requestedColumns, int firstTabIndex) {
		setCaption(caption);
		setWidth(width);
		this.requestedColumns = requestedColumns;
		this.firstTabIndex = firstTabIndex;
		
		if (requestedColumns < 1) {
			throw new IllegalArgumentException("The requestedColumn must be greater than 0!");
		}
		
		init();
	}

	public int getFirstTabIndex() {
		return firstTabIndex;
	}

	public int getLastTabIndex() {
		int c = 0;
		for (Layout l : columns) {
			c += getComponentCount(l);
		}
		return getFirstTabIndex() + c - 1;
	} 
	
	private int getComponentCount(Layout l) {
		Iterator<Component> components = l.getComponentIterator();
		int i = 0;
		while (components.hasNext()) {
			Component c = components.next();
			if (c instanceof Focusable)
				i++;
		}
		return i;
	}

	public int getColumnsCount() {
		return requestedColumns;
	}

	private void init() {		
        HorizontalLayout hLayout;
        hLayout= new HorizontalLayout();
        hLayout.setWidth("100%");
        hLayout.setSpacing(true);
        addComponent(hLayout);        
        
        String alignment = "left";
        for (int i = 0; i < requestedColumns; i++) {
        	if (i > 0) {
        		alignment = "middle";
        		if (i == (requestedColumns - 1)) {
        			alignment = "right";
        		}
        	}
        	columns.add(createFormLayout(hLayout, i, alignment));
        }
	}

	public CustomTabIndexFormLayout createFormLayout(HorizontalLayout parent, int index, String alignment) {
		CustomTabIndexFormLayout fLayout = new CustomTabIndexFormLayout(
				parent,
				getFirstTabIndex() + index, 
				requestedColumns);		
		fLayout.setWidth("98%");
		parent.addComponent(fLayout);
		parent.setComponentAlignment(fLayout, alignment);
		return fLayout;
	}

	public FormLayout getColumn(int index) {
		return columns.get(index);
	}
	
	public class CustomTabIndexFormLayout extends FormLayout {
		private static final long serialVersionUID = 2827505355429299323L;
		
		private int firstComponentIndex = 0;
		private int increment = 0;
		public CustomTabIndexFormLayout(HorizontalLayout parent, int firstComponentIndex, int increment) {
			this.firstComponentIndex = firstComponentIndex;
			this.increment = increment;
		}
		
		@Override
		public void addComponent(Component c, int index) {
			super.addComponent(c, index);
			refreshTabIndexes();			
		}

		@Override
		public void addComponent(Component c) {
			super.addComponent(c);
			updateTabIndex(c, components.size() - 1);			
		}

		@Override
		public void addComponentAsFirst(Component c) {
			super.addComponentAsFirst(c);
			refreshTabIndexes();
		}

		public void refreshTabIndexes() {
			int i = 0;
			for (Component c : components) {
				updateTabIndex(c, i++);
			}			
		}

		private void updateTabIndex(Component c, int index) {
			if (c instanceof Focusable) {
				Focusable f = (Focusable) c;
				Component current = null;
				Focusable currentFocusable = null;
				for (int i = index-1; i > -1; i--) { 
					current = components.get(i);
					if ((!current.equals(c)) && (current instanceof Focusable)) {
						currentFocusable = (Focusable) current;
						break;
					}
				}
				
				if (Check.notNull(currentFocusable)) {
					f.setTabIndex((currentFocusable.getTabIndex() + increment));					
				}
				else {
					f.setTabIndex(firstComponentIndex);
				}
			}
		}

	}
}

usage:


        VerticalLayout vLayout = new VerticalLayout();
        vLayout.setWidth(MAX_WIDTH);       
        main.addComponent(vLayout);

        int tabIndex = 1;
       
        tabIndex = populateDebugPanel(vLayout, tabIndex);
        
        GebTitlebar title = new GebTitlebar(main.getCaption());
        vLayout.addComponent(title);
        
        DetailPanel partyPanel = new DetailPanel("Ordinante " + tabIndex, MAX_WIDTH, 2, tabIndex);
        populatePartyPanelLeft(partyPanel.getColumn(0));
        populatePartyPanelRight(partyPanel.getColumn(1));
        tabIndex = partyPanel.getLastTabIndex() + 1;        
        vLayout.addComponent(partyPanel);
        
        DetailPanel counterpartyPanel = new DetailPanel("Debitore " + tabIndex, MAX_WIDTH, 2, tabIndex);
        populateCounterpartyLeft(counterpartyPanel.getColumn(0));
        populateCounterpartyRight(counterpartyPanel.getColumn(1));
        tabIndex = counterpartyPanel.getLastTabIndex() + 1;
        vLayout.addComponent(counterpartyPanel);

Cheers,

Ivan

Hi Ivan,

thanks for sharing.

I currently don’t have the need for such a panel, but it’s always good to have such customizations shared via the forum.

Haven’t give it a test drive, but is the idea that it will take the list of fields/properties for a given item, say in detail edit mode, so that they are listed in more than a single column like a tradition Form does? That sounds useful, especially if you can set the order of the fields and any colspans that may be needed so that you can layout the Form as a Grid of some sort. Don’t know if Form does that already since I’m very new and only playing with the tutorial.

Thanks for sharing.

It give you this kind of layout, but it is not limited to 2 columns.

label1 textField1 label 2 textField2
label3 textField3 label4 textField4

and so on.

Ivan

Might be easy to do with Vaadin 7:


	public class MultiColumnFormLayout extends GridLayout
	{
		private final int rows;
		private int componentCount;
		
		/**
		 * A layout that uses FormLayout in multiple columns.
		 * @param columns number of columns (with nested components) to display.
		 * @param componentCount the pending number of nested components (or input fields).
		 */
		public MultiColumnFormLayout(int columns, int componentCount) {
			super(columns, 1);	// will make as many FormLayouts as columns were defined
			
			final int additionalRow = (componentCount % columns == 0) ? 0 : 1;
			this.rows = (componentCount / columns) + additionalRow;
			
			for (int i = 0; i < columns; i++)	{
				final FormLayout formLayout = new FormLayout();
				
				// use super.addComponent() because this.addComponent() is overridden to throw UnsupportedOperationException
				super.addComponent(formLayout, i, 0, i, 0);
			}
		}
		
		@Override
		public void addComponent(Component component) {
			final int column = componentCount / rows;
			componentCount++;
			final FormLayout formLayout = (FormLayout) getComponent(column, 0);
			formLayout.addComponent(component);
		}
		
		@Override
		public void addComponent(Component component, int column1, int row1, int column2, int row2) throws OverlapsException, OutOfBoundsException {
			throw new UnsupportedOperationException("Sorry, this is a FormLayout delegate!");
		}
	}

Olá

This is a very old discussion. Since you has raised it, I have questions.

Please, could you show the usage? Does it is possible to bind a Container?

I want to raise this up too.

I look at the code of FormLayout.

I think it will be possible to create a Multi column form layout.

the issue is when you put a formlayout for each component in the a Gridlayout is that your caption and values are not alligned verticaly.

(In a forlayout your caption are alligned on the right and your values on the left)

Currently I also solved this by creating a custom layout based on a table.

@Benoit: Can you give an example, please?

Seems odd, given how many people want to do this. Hope its easier in Vaadin 7.4

So if I want to do a form layout where SOME of the rows have another field, but not all. How should I do this?
Each row should spread to take 100 percent of width, so the rows with extra fields split that in half.

e.g.

label1 field1
label2 field2 label3 field3
label4 field4 label5 field5
label6 field6

Hmm, I see mention of GridFormLayout

http://dev.vaadin.com/ticket/6957

Where is it?

Thanks.

I would like this functionality also. I am using Vaadin 7.5.9 and I dont see GridFormLayout anywhere :frowning:

Sam here