All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.metawidget.gwt.client.ui.layout.FlexTableLayout Maven / Gradle / Ivy

The newest version!
// Metawidget
//
// This file is dual licensed under both the LGPL
// (http://www.gnu.org/licenses/lgpl-2.1.html) and the EPL
// (http://www.eclipse.org/org/documents/epl-v10.php). As a
// recipient of Metawidget, you may choose to receive it under either
// the LGPL or the EPL.
//
// Commercial licenses are also available. See http://metawidget.org
// for details.

package org.metawidget.gwt.client.ui.layout;

import static org.metawidget.inspector.InspectionResultConstants.*;

import java.util.HashMap;
import java.util.Map;

import org.metawidget.gwt.client.ui.Facet;
import org.metawidget.gwt.client.ui.GwtMetawidget;
import org.metawidget.gwt.client.ui.Stub;
import org.metawidget.layout.iface.AdvancedLayout;
import org.metawidget.util.simple.SimpleLayoutUtils;
import org.metawidget.util.simple.StringUtils;

import com.google.gwt.user.client.ui.ComplexPanel;
import com.google.gwt.user.client.ui.FlexTable;
import com.google.gwt.user.client.ui.FlexTable.FlexCellFormatter;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.Panel;
import com.google.gwt.user.client.ui.Widget;

/**
 * Layout to arrange widgets in a FlexTable, with one column for labels and another for
 * the widget.
 *
 * @author Richard Kennard
 */

public class FlexTableLayout
	implements AdvancedLayout {

	//
	// Private statics
	//

	private static final int	LABEL_AND_COMPONENT_AND_REQUIRED	= 3;

	//
	// Private members
	//

	private final int			mNumberOfColumns;

	private final String		mTableStyleName;

	private final String[]		mColumnStyleNames;

	private final String		mFooterStyleName;

	//
	// Constructor
	//

	public FlexTableLayout() {

		this( new FlexTableLayoutConfig() );
	}

	public FlexTableLayout( FlexTableLayoutConfig config ) {

		mNumberOfColumns = config.getNumberOfColumns();
		mTableStyleName = config.getTableStyleName();
		mColumnStyleNames = config.getColumnStyleNames();
		mFooterStyleName = config.getFooterStyleName();
	}

	//
	// Public methods
	//

	public void onStartBuild( GwtMetawidget metawidget ) {

		// Do nothing
	}

	public void startContainerLayout( Panel container, GwtMetawidget metawidget ) {

		State state = getState( container, metawidget );
		FlexTable flexTable = new FlexTable();
		flexTable.setStyleName( mTableStyleName );
		container.add( flexTable );

		state.formatter = flexTable.getFlexCellFormatter();
	}

	public void layoutWidget( Widget widget, String elementName, Map attributes, Panel container, GwtMetawidget metawidget ) {

		// Do not render empty stubs

		if ( widget instanceof Stub && ( (Stub) widget ).getWidgetCount() == 0 ) {
			return;
		}

		// Calculate row and actualColumn. Note FlexTable doesn't work quite as might be
		// expected. Specifically, it doesn't understand 'colspan' in relation to previous rows. So
		// if you do...
		//
		// layout.setWidget( row, 0, widget1 );
		// layout.setColSpan( row, 0, 2 );
		// layout.setWidget( row, 2, widget2 );
		//
		// ...you'll actually get...
		//
		// widget1
		// 
		// widget2
		//
		// ...note FlexTable inserts an extra  because it thinks column 1 is missing. Therefore
		// the actualColumn is always just the next column, regardless of what state.mCurrentColumn
		// is

		int actualColumn;
		FlexTable flexTable = (FlexTable) ( (ComplexPanel) container ).getWidget( 0 );
		int row = flexTable.getRowCount();

		int numberOfColumns = getEffectiveNumberOfColumns( container );

		State state = getState( container, metawidget );

		if ( state.currentColumn < numberOfColumns && row > 0 ) {
			row--;
			actualColumn = flexTable.getCellCount( row );
		} else {
			state.currentColumn = 0;
			actualColumn = 0;
		}

		// Special support for large components

		boolean spanAllColumns = willFillHorizontally( widget, attributes );

		if ( spanAllColumns && state.currentColumn > 0 ) {
			state.currentColumn = 0;
			actualColumn = 0;
			row++;
		}

		// Label

		String labelText = metawidget.getLabelString( attributes );

		if ( SimpleLayoutUtils.needsLabel( labelText, elementName ) ) {

			// Note: GWT Labels are not real HTML labels, and have no 'for' attribute

			Label label = new Label( labelText + StringUtils.SEPARATOR_COLON );
			String styleName = getStyleName( state.currentColumn * LABEL_AND_COMPONENT_AND_REQUIRED, metawidget );

			if ( styleName != null ) {
				state.formatter.setStyleName( row, actualColumn, styleName );
			}

			flexTable.setWidget( row, actualColumn, label );
		}

		// Widget

		// Widget column (null labels get collapsed, blank Strings get preserved)

		if ( labelText != null ) {
			// Zero-column layouts need an extra row

			if ( numberOfColumns == 0 ) {
				state.currentColumn = 0;
				actualColumn = 0;
				row++;
			} else {
				actualColumn++;
			}
		}

		String styleName = getStyleName( ( state.currentColumn * LABEL_AND_COMPONENT_AND_REQUIRED ) + 1, metawidget );

		if ( styleName != null ) {
			state.formatter.setStyleName( row, actualColumn, styleName );
		}

		flexTable.setWidget( row, actualColumn, widget );

		// Colspan

		int colspan;

		// Metawidgets and large components span all columns

		if ( spanAllColumns ) {
			colspan = ( numberOfColumns * LABEL_AND_COMPONENT_AND_REQUIRED ) - 2;
			state.currentColumn = numberOfColumns;

			if ( labelText == null ) {
				colspan++;
			}

			// Metawidgets span the required column too

			if ( widget instanceof GwtMetawidget ) {
				colspan++;
			}
		} else if ( labelText == null ) {

			// Components without labels span two columns

			colspan = 2;
		} else {
			
			// Everyone else spans just one
			
			colspan = 1;
		}

		if ( colspan > 1 ) {
			state.formatter.setColSpan( row, actualColumn, colspan );
		}

		// Required

		if ( !( widget instanceof GwtMetawidget ) ) {
			layoutRequired( attributes, container, metawidget );
		}

		state.currentColumn++;
	}

	public void endContainerLayout( Panel container, GwtMetawidget metawidget ) {

		// Do nothing
	}

	public void onEndBuild( GwtMetawidget metawidget ) {

		Facet facet = metawidget.getFacet( "buttons" );

		if ( facet != null ) {
			State state = getState( metawidget, metawidget );
			FlexTable flexTable = (FlexTable) metawidget.getWidget( 0 );
			int row = flexTable.getRowCount();

			int numberOfColumns = getEffectiveNumberOfColumns( metawidget );

			if ( numberOfColumns > 0 ) {
				state.formatter.setColSpan( row, 0, numberOfColumns * LABEL_AND_COMPONENT_AND_REQUIRED );
			}

			if ( mFooterStyleName != null ) {
				state.formatter.setStyleName( row, 0, mFooterStyleName );
			}

			flexTable.setWidget( row, 0, facet );
		}
	}

	//
	// Protected methods
	//

	protected void layoutRequired( Map attributes, Widget container, GwtMetawidget metawidget ) {

		State state = getState( container, metawidget );
		FlexTable flexTable = (FlexTable) ( (ComplexPanel) container ).getWidget( 0 );
		int row = flexTable.getRowCount() - 1;
		int column = flexTable.getCellCount( row );

		state.formatter.setStyleName( row, column, getStyleName( ( state.currentColumn * LABEL_AND_COMPONENT_AND_REQUIRED ) + 2, metawidget ) );

		if ( attributes != null && TRUE.equals( attributes.get( REQUIRED ) ) && !TRUE.equals( attributes.get( READ_ONLY ) ) && !metawidget.isReadOnly() ) {
			flexTable.setText( row, column, "*" );
			return;
		}

		// Render an empty div, so that the CSS can force it to a certain
		// width if desired for the layout (browsers seem to not respect
		// widths set on empty table columns)
		//
		// Note: don't do 
, as we may not be XHTML flexTable.setHTML( row, column, "
" ); } /** * @param metawidget * the Metawidget doing the layout */ protected String getStyleName( int styleName, GwtMetawidget metawidget ) { if ( mColumnStyleNames == null || mColumnStyleNames.length <= styleName ) { return null; } return mColumnStyleNames[styleName]; } protected boolean willFillHorizontally( Widget widget, Map attributes ) { if ( widget instanceof GwtMetawidget ) { return true; } return SimpleLayoutUtils.isSpanAllColumns( attributes ); } // // Private methods // /** * Get the number of columns to use in the layout. *

* Nested Metawidgets are always just single column. */ private int getEffectiveNumberOfColumns( Widget container ) { if ( container.getParent() instanceof FlexTable && container.getParent().getParent() instanceof GwtMetawidget ) { return 1; } return mNumberOfColumns; } private State getState( Widget container, GwtMetawidget metawidget ) { @SuppressWarnings( "unchecked" ) Map stateMap = (Map) metawidget.getClientProperty( getClass() ); if ( stateMap == null ) { stateMap = new HashMap(); metawidget.putClientProperty( getClass(), stateMap ); } State state = stateMap.get( container ); if ( state == null ) { state = new State(); stateMap.put( container, state ); } return state; } // // Inner class // /** * Simple, lightweight structure for saving state. */ /* package private */static class State { /* package private */FlexCellFormatter formatter; /* package private */int currentColumn; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy