Build the User Interface

At this point, we've built the basic layout for myCRM. Now we're ready to start building the user interface using smartGWT widgets. In this post, we'll:

  1. Select the smartGWT widgets for each UI element
  2. Implement the smartGWT widgets
  3. Test the application in development mode

Like GWT, smartGWT shields you from worrying too much about cross-browser incompatibilities. If you construct your application's user interface with smartGWT widgets, the application will work on the most recent versions of Chrome, Firefox, Internet Explorer, Opera, and Safari. However, it's still a good idea to test your applications thoroughly on every browser.

1. Select the smartGWT widgets

Take a look at the smartGWT Showcase, the types of widgets and how they can be used. Some of the widgets that we might find useful are the menus, the section stack and the list grid.

2. Implement the smartGWT widgets

In the area to the left of the browser window we've decided to place a Navigation pane. The navigation pane is where you place information that enables you to move around within an application.

So let's start out by updating the Navigation pane:

package au.org.myCRM.client.ui.widgets;

import com.google.gwt.core.client.GWT;
import com.smartgwt.client.types.Overflow;
import com.smartgwt.client.types.VisibilityMode;
import com.smartgwt.client.widgets.HTMLFlow;
import com.smartgwt.client.widgets.layout.SectionStack;
import com.smartgwt.client.widgets.layout.SectionStackSection;

public class NavigationPane extends SectionStack {
	
  private static final int WEST_WIDTH = 200;
	
  public NavigationPane() {
    super();
				
    GWT.log("init NavigationPane()...", null);
	
    // initialise the Section Stack
    this.setWidth(WEST_WIDTH);
    this.setVisibilityMode(VisibilityMode.MUTEX);
    this.setShowExpandControls(false);
    this.setAnimateSections(true);	

    // create sample content for the Section Stack sections
    String sales = "Leads<br />Opportunities<br />Accounts<br />Contacts<br />" +
    			   "Marketing Lists<br />Competitors<br />Products<br />" + 
    			   "Sales Literature<br />Quotes<br />Orders<br />Quick Campaigns";
   
    String settings = "Administration<br />Business Management<br />Customisation<br />" +
    				  "Templates<br />Product Catalogue<br />Workflows<br />" +
    				  "Data Management<br />System Jobs<br />"; 

    String resources = "Highlights<br />Sales<br />Marketing<br />Service<br />Settings<br />";

    // initialise the Sales section 
    SectionStackSection section1 = new SectionStackSection("Sales"); 
    section1.setExpanded(true); 
    HTMLFlow htmlFlow1 = new HTMLFlow();  
    htmlFlow1.setOverflow(Overflow.AUTO);  
    htmlFlow1.setPadding(10);  
    htmlFlow1.setContents(sales);     
    section1.addItem(htmlFlow1); 
    
    // initialise the Settings section 
    SectionStackSection section2 = new SectionStackSection("Settings");  
    section2.setExpanded(true);  
    HTMLFlow htmlFlow2 = new HTMLFlow();  
    htmlFlow2.setOverflow(Overflow.AUTO);  
    htmlFlow2.setPadding(10);  
    htmlFlow2.setContents(settings);  
    section2.addItem(htmlFlow2); 
    
    // initialise the Resource Centre section 
    SectionStackSection section3 = new SectionStackSection("Resource Centre");  
    section3.setExpanded(true);
    HTMLFlow htmlFlow3 = new HTMLFlow();  
    htmlFlow3.setOverflow(Overflow.AUTO);  
    htmlFlow3.setPadding(10);  
    htmlFlow3.setContents(resources);    
    section3.addItem(htmlFlow3); 
   
    // add the sections to the Section Stack
    this.addSection(section1);  
    this.addSection(section2);  
    this.addSection(section3);   
  }	
}

We now have a UI element, the SectionStack that will help us to organise information in the Navigation pane.

 

Now, lets add a grid to the Context area by creating a new view class, the AccountView:

package au.org.myCRM.client.ui.view;

import au.org.myCRM.client.ui.widgets.ContextAreaListGrid;

import com.google.gwt.core.client.GWT;
import com.smartgwt.client.widgets.layout.VLayout;

public class AccountView extends VLayout {
	
  public AccountView() {
	super();
		
	GWT.log("init AccountView()...", null);
		
	// initialise the Account View layout container
    this.addStyleName("crm-ContextArea");
    this.setWidth("*"); 
    
    // add the List Grid to the Account View layout container
    this.addMember(new ContextAreaListGrid());
  }
}

Then we need to create a wrapper for the ListGrid widget, the ContextAreaListGrid class:

package au.org.myCRM.client.ui.widgets;

import au.org.myCRM.client.ui.data.AccountData;

import com.google.gwt.core.client.GWT;
import com.smartgwt.client.types.Alignment;
import com.smartgwt.client.widgets.grid.ListGrid;
import com.smartgwt.client.widgets.grid.ListGridField;
import com.smartgwt.client.types.ListGridFieldType;

public class ContextAreaListGrid extends ListGrid {

  public ContextAreaListGrid() {
	super();
		   
	GWT.log("init ContextAreaListGrid()...", null);

    // initialise the List Grid
	this.setShowAllRecords(true);  	
	this.setSortField(1); 
	       
    // initialise the List Grid fields	
	ListGridField iconField = new ListGridField("icon", "#", 27);
	iconField.setImageSize(16); 
	iconField.setAlign(Alignment.CENTER);
	iconField.setType(ListGridFieldType.IMAGE);  
	iconField.setImageURLPrefix("icons/16/");  
	iconField.setImageURLSuffix(".png");  
		
	ListGridField accountNameField = new ListGridField("accountName", "Account Name", 320);  
	ListGridField mainPhoneField = new ListGridField("mainPhone", "Main Phone", 100);  
	ListGridField locationField = new ListGridField("location", "Location", 100);  
	ListGridField primaryContactField = new ListGridField("primaryContact", "Primary Contact", 140); 
	primaryContactField.setType(ListGridFieldType.LINK);  
	ListGridField emailPrimaryContactField = new ListGridField("emailPrimaryContact", 
															   "Email (Primary Contact)", 180);  	
	ListGridField emptyField = new ListGridField("emptyField", " ");  	
		
    // set the fields into the List Grid	
	this.setFields(new ListGridField[] {iconField, accountNameField, mainPhoneField, locationField, 
				   primaryContactField, emailPrimaryContactField, emptyField });  
	
	// populate the List Grid
	this.setData(AccountData.getRecords());  
  }
}

We also need to create a couple of supporting classes, the AccountData class:

package au.org.myCRM.client.ui.data;

public class AccountData {

  private static AccountRecord[] records;

  public static AccountRecord[] getRecords() {
	if (records == null) {
	  records = getNewRecords();
	}
	return records;
  }

  public static AccountRecord[] getNewRecords() {
	return new AccountRecord[]{
	  new AccountRecord("account", "ABC Pty Ltd", "(02) 251 6641", "Sydney", 
			  			"Sean Doyle", "sales@uptick.com.au"),
	  new AccountRecord("account", "DEF Pty Ltd", "(02) 251 6642", "Melbourne",
			  			"Mario Bernatovic", "sales@uptick.com.au"),
	  new AccountRecord("account", "GHI Pty Ltd", "(02) 251 6643", "Newcastle",
			  			"Rob Ferguson", "sales@uptick.com.au"),
	  new AccountRecord("account", "JKL Pty Ltd", "(02) 251 6644", "Perth",
			  			"Alister Bennett", "sales@uptick.com.au"),
	  new AccountRecord("account", "MNO Pty Ltd", "(02) 251 6645", "Newcastle",
			  			"Mark Kirkpatrick", "sales@uptick.com.au"),
	  new AccountRecord("account", "PQR Pty Ltd", "(02) 251 6647", "Bankstown",
			  			"Grahame King", "sales@uptick.com.au"),
	  new AccountRecord("account", "STU Pty Ltd", "(02) 251 6648", "Sydney",
			  			"Peter Wood", "sales@uptick.com.au"),
	  new AccountRecord("account", "VWX Pty Ltd", "(02) 251 6649", "Newcastle",
			  			"Ross Hodge", "sales@uptick.com.au"),
	  new AccountRecord("account", "YZA Pty Ltd", "(02) 251 6610", "Sydney",
			  			"Darren Poyner", "sales@uptick.com.au"),
	  new AccountRecord("account", "123 Pty Ltd", "(02) 251 6611", "Glebe",
			  			"Carl Blick", "sales@uptick.com.au"),
	  new AccountRecord("account", "456 Pty Ltd", "(02) 251 6612", "Sydney",
			  			"Mark Powrie", "sales@uptick.com.au"),
	  new AccountRecord("account", "789 Pty Ltd", "(02) 251 6613", "Perth",
			  			"Jason Bance", "sales@uptick.com.au"),
	  new AccountRecord("account", "101 Pty Ltd", "(02) 251 6614", "Newcastle",
			  			"Patrick Keegan", "sales@uptick.com.au")
	};
  }
}

and the AccountRecord class:

package au.org.myCRM.client.ui.data;

import com.smartgwt.client.widgets.grid.ListGridRecord;

public class AccountRecord extends ListGridRecord {
	
  public AccountRecord() {}
	
  public AccountRecord(String icon, 
		  			   String accountName,
					   String mainPhone,
    				   String location,
    				   String primaryContact,
    				   String emailPrimaryContact) {
  setIcon(icon);
  setAccountName(accountName);
  setMainPhone(mainPhone);
  setLocation(location);
  setPrimaryContact(primaryContact);
  setEmailPrimaryContact(emailPrimaryContact);  
  }	
    
  public void setIcon(String icon) {
   setAttribute("icon", icon);
  }       
    
  public void setAccountName(String accountName) {
    setAttribute("accountName", accountName);
  }

  public void setMainPhone(String mainPhone) {
    setAttribute("mainPhone", mainPhone);
  }
    
  public void setLocation(String location) {
    setAttribute("location", location);
  }    
    
  public void setPrimaryContact(String primaryContact) {
    setAttribute("primaryContact", primaryContact);
  }   
    
  public void setEmailPrimaryContact(String emailPrimaryContact) {
    setAttribute("emailPrimaryContact", emailPrimaryContact);
  }        
    
  public String getIcon() {
    return getAttributeAsString("icon");
  }    

  public String getAccountName() {
    return getAttributeAsString("accountName");
  }
    
  public String getMainPhone() {
    return getAttributeAsString("mainPhone");
  }
  
  public String getLocation() {
    return getAttributeAsString("location");
  }    

  public String getPrimaryContact() {
    return getAttributeAsString("primaryContact");
  }
    
  public String getEmailPrimaryContact() {
    return getAttributeAsString("emailPrimaryContact");
  }    
}

We also need to update the applications's style sheet, myCRM.css to include a background image and some padding around the ListGrid in the Context area.

/*
 * Add css rules here for your application.
 *
 * Note: margin, border, padding, content (the box model)
 *
 */

* {
  font-family: Tahoma, Verdana, sans-serif;
  font-size: 11px;
}

.crm-ContextArea {
  background-image: url(images/context_area.png);
  background-repeat: repeat-x;  
  
  padding-top: 4px;
  padding-right: 4px;
  padding-bottom: 4px;
  padding-left: 4px;  
}

Tip: Make sure you put the applications resources where smartGWT can find them at runtime (e.g. in an images subdirectory in the war directory).

We now have a UI element, the ListGrid that will help us to display information in the Context area.

 

Now, lets update the Application Menu class by adding support for a MenuBar, Menus and MenuItems.

package au.org.myCRM.client.ui.widgets;

import com.google.gwt.core.client.GWT;
import com.smartgwt.client.widgets.layout.HLayout;
import com.smartgwt.client.widgets.menu.Menu;
import com.smartgwt.client.widgets.menu.MenuBar;
import com.smartgwt.client.widgets.menu.MenuItem;
import com.smartgwt.client.widgets.menu.MenuItemSeparator;

public class ApplicationMenu extends HLayout {
	
  private static final int APPLICATION_MENU_HEIGHT = 27;
  private static final int DEFAULT_SHADOW_DEPTH = 10;
  
  private static final String SEPARATOR = "separator";
  private static final String ICON_PREFIX = "icons/16/";
  private static final String ICON_SUFFIX = ".png";
  
  private MenuBar menuBar ;
  private int menuPosition = 0;    
  	
  public ApplicationMenu() {
	super();
			
	GWT.log("init ApplicationMenu()...", null);
		
    // initialise the Application Menu layout container
	this.addStyleName("crm-ApplicationMenu");	
    this.setHeight(APPLICATION_MENU_HEIGHT);

    // initialise the Menu Bar
	menuBar = new MenuBar();
	
	// add the Menu Bar to the Application Menu layout container
	this.addMember(menuBar);  	    
  }
  
  public Menu addMenu(String menuName, int width, String menuItemNames) {
	// initialise the new menu
	Menu menu = new Menu(); 
	menu.setTitle(menuName);
	menu.setShowShadow(true);  
	menu.setShadowDepth(DEFAULT_SHADOW_DEPTH); 
	menu.setWidth(width);
	
	// create an array of menu item names 
	String[] menuItems = process(menuItemNames);
	
	for (int i = 0; i < menuItems.length; i++) {
	  // remove any whitespace
	  String menuItemName = menuItems[i].replaceAll("\\W", "");

	  if (menuItemName.contentEquals(SEPARATOR)) {
	    MenuItemSeparator separator = new MenuItemSeparator();
	    menu.addItem(separator);  
	    continue;
	  }
		  
	  MenuItem menuItem = new MenuItem(menuItems[i], getIcon(menuItems[i])); 
	  // menuItem.addClickHandler(clickHandler);
	  menu.addItem(menuItem);    
	}
		
	Menu[] menus = new Menu[1]; 
	menus[0] = menu;
	menuBar.addMenus(menus, menuPosition);
	menuPosition++ ;
	
	return menus[0];
  }
	  
  public Menu addMenu(String menuName, int width) {
	// initialise the new menu
	Menu menu = new Menu(); 
	menu.setTitle(menuName);
	menu.setShowShadow(true);  
	menu.setShadowDepth(DEFAULT_SHADOW_DEPTH); 
	menu.setWidth(width);
		
	Menu[] menus = new Menu[1]; 
	menus[0] = menu;
	menuBar.addMenus(menus, menuPosition);	
	menuPosition++ ;
		
	return menu;
  }
	  
  public Menu addSubMenu(Menu parentMenu, String subMenuName, String menuItemNames) {
	// initialise the new sub menu
	Menu menu = new Menu(); 
	menu.setShowShadow(true);  
	menu.setShadowDepth(DEFAULT_SHADOW_DEPTH); 
			
	MenuItem menuItem = new MenuItem(subMenuName);
	
	// create an array of menu item names 
	String[] menuItems = process(menuItemNames);

	for (int i = 0; i < menuItems.length; i++) {
	  // remove any whitespace
	  String menuItemName = menuItems[i].replaceAll("\\W", "");
			  
	  if (menuItemName.contentEquals(SEPARATOR)) {
	    MenuItemSeparator separator = new MenuItemSeparator();
	    menu.addItem(separator);  
	    continue;
	  }
			  
	  menuItem = new MenuItem(menuItems[i], getIcon(menuItems[i])); 
	  // menuItem.addClickHandler(clickHandler);
	  menu.addItem(menuItem);    
	}
			
	// add the sub menu to the menu item
	menuItem = new MenuItem(subMenuName);
	parentMenu.addItem(menuItem); 
	menuItem.setSubmenu(menu);
			
	return menu;
  }  
	  
  public final static String DELIMITER = ",";
	  
  public static String[] process(String line) {
    return line.split(DELIMITER);
  }
	  
  private String getIcon(String applicationName) {
	// remove any whitespace
	String name = applicationName.replaceAll("\\W", "");
	// e.g. "icons/16/" + "activities" + ".png"	
	String icon = ICON_PREFIX + name.toLowerCase() + ICON_SUFFIX ; 
	return icon ;
  }
}

We also need to add the following to the applications's style sheet, myCRM.css:

/*
 * Add css rules here for your application.
 *
 * Note: margin, border, padding, content (the box model)
 *
 */

* {
  font-family: Tahoma, Verdana, sans-serif;
  font-size: 11px;
}

body, table td, select {
  font-family: Tahoma, Verdana, sans-serif;
  font-size: 11px;
}

.crm-ContextArea {
  border-top: 1px solid #7598c7;	
	
  background-image: url(images/context_area.png);
  background-repeat: repeat-x;  
  
  padding-top: 4px;
  padding-right: 4px;
  padding-bottom: 4px;
  padding-left: 4px;  
}

.crm-ApplicationMenu {
  background-image: url(images/application_menu.png);
  background-repeat: repeat-x; 
  
  padding-top: 4px;
  padding-left: 4px;
}

/*
 * Application Menu buttons
 */
 
.menuButton,
.menuButtonOver,
.menuButtonDown,
.menuButtonDisabled,
.menuButtonSelected,
.menuButtonSelectedDown,
.menuButtonSelectedOver,
.menuButtonSelectedDisabled {
  border: transparent 1px solid; 	
  background: none;
  color: #15428b;   
  font-family: Tahoma, Verdana, sans-serif;
  font-size: 11px;
  font-weight: normal;  
  padding-left: 4px;
  padding-right: 4px;
}
 
.menuButtonOver {
  border: #ffdb6c 1px solid;   
  background-image: url(images/application_menu_button_over.png);
  background-repeat: repeat-x;    
  color: #15428b;   
  cursor: default;
  font-family: Tahoma, Verdana, sans-serif;
  font-size: 11px;
  font-weight: normal;  
  padding-left: 2px;
  padding-right: 2px;
}

.menuButtonDown,
.menuButtonSelected,
.menuButtonSelectedDown,
.menuButtonSelectedOver {
  border: #ffdb6c 1px solid;   
  background-image: url(images/application_menu_button_down.png);
  background-repeat: repeat-x;    
  color: #15428b;   
  cursor: default;
  font-family: Tahoma, Verdana, sans-serif;
  font-size: 11px;
  font-weight: normal;  
  padding-left: 2px;  
  padding-right: 2px;
}

.menu,
.menuSelected,
.menuOver,
.menuSelectedOver,
.menuDisabled,
.menuTitleField,
.menuTitleFieldSelected,
.menuTitleFieldOver,
.menuTitleFieldSelectedOver,
.menuTitleFieldDisabled,
.menuIconField,
.menuIconFieldOver,
.menuIconFieldSelected,
.menuIconFieldSelectedOver,
.menuIconFieldDisabled,
.treeMenuSelected,
.treeMenuSelectedOver,
.treeMenuSelectedSelected,
.treeMenuSelectedSelectedOver { 
  color: #15428b;
  font-family: Tahoma, Verdana, sans-serif;
  font-size: 11px;
  font-weight: normal;  	
  padding-top: 2px; 
  padding-bottom: 2px;
}

and update the application's entry point class as follows:

public class myCRM implements EntryPoint {
	
  private static final int NORTH_HEIGHT = 85; // MASTHEAD_HEIGHT + APPLICATION_MENU_HEIGHT
  private static final int DEFAULT_MENU_WIDTH = 70;
  
  private VLayout mainLayout;	
  private HLayout northLayout;  
  private HLayout southLayout;
  private VLayout eastLayout;  
  private VLayout westLayout;
  
  ApplicationMenu applicationMenu ;
  
  interface GlobalResources extends ClientBundle {
	@NotStrict
	@Source("myCRM.css")
	CssResource css();
  }  
	
  public void onModuleLoad() {
	  
	GWT.log("init OnLoadModule()...", null);  
	
	// inject global styles
	GWT.<GlobalResources>create(GlobalResources.class).css().ensureInjected();	
	
	// get rid of scroll bars, and clear out the window's built-in margin,
    // because we want to take advantage of the entire client area
    Window.enableScrolling(false);
    Window.setMargin("0px");
    
    // initialise the main layout container
    mainLayout = new VLayout();
    mainLayout.setWidth100();  
    mainLayout.setHeight100();  
    
    // initialise the North layout container
    northLayout = new HLayout();  
    northLayout.setHeight(NORTH_HEIGHT); 
    
    VLayout vLayout = new VLayout(); 
    // add the Masthead to the nested layout container
    vLayout.addMember(new Masthead());
    
    // intiialise the Application menu
    applicationMenu = new ApplicationMenu();
    applicationMenu.addMenu("<u>N</u>ew Activity", DEFAULT_MENU_WIDTH, 
    						"Task, Fax, Phone Call, Email, Letter, " + 
    						"Appointment");
    applicationMenu.addMenu("New Re<u>c</u>ord", DEFAULT_MENU_WIDTH, 
							"Account, Contact, separator, Lead, Opportunity");
    Menu goToMenu = applicationMenu.addMenu("<u>G</u>o To", DEFAULT_MENU_WIDTH - 30);
    applicationMenu.addSubMenu(goToMenu, "Sales", "Leads, Opportunities, Accounts, Contacts");
    applicationMenu.addSubMenu(goToMenu, "Settings", "Administration, Templates, Data Management");
    applicationMenu.addSubMenu(goToMenu, "Resource Centre", "Highlights, Sales, Settings");
    applicationMenu.addMenu("<u>T</u>ools", DEFAULT_MENU_WIDTH - 30, 
							"Import Data, Duplicate Detection, Advanced Find, Options");
    applicationMenu.addMenu("<u>H</u>elp", DEFAULT_MENU_WIDTH - 30, 
							"Help on this Page, Contents, myCRM Online, About myCRM");

    // add the Application Menu to the nested layout container
    vLayout.addMember(applicationMenu);

    // add the nested layout container to the North layout container
    northLayout.addMember(vLayout);
    
    // initialise the West layout container
    westLayout = new NavigationPane();
    // initialise the East layout container
    eastLayout = new AccountView(); 
   
    // initialise the South layout container
    southLayout = new HLayout(); 
 
    // set the Navigation Pane and Context Area as members of the South 
    // layout container 
    southLayout.setMembers(westLayout, eastLayout);  
    
    // add the North and South layout containers to the main layout container
    mainLayout.addMember(northLayout);  
    mainLayout.addMember(southLayout); 

    // add the main layout container to GWT's root panel 
    RootLayoutPanel.get().add(mainLayout);  	  
  }
}

Tip: If you are looking for open source icons then make sure you check out the Silk Icons library by Mark James.

We now have a few more UI elements: a MenuBar, Menus and MenuItems.

 

Now, lets update the Masthead by adding support for a logo:

package au.org.myCRM.client.ui.widgets;

import com.google.gwt.core.client.GWT;
import com.smartgwt.client.types.Alignment;
import com.smartgwt.client.widgets.Img;
import com.smartgwt.client.widgets.Label;
import com.smartgwt.client.widgets.layout.HLayout;

public class Masthead extends HLayout {

  private static final int MASTHEAD_HEIGHT = 58;
    
  public Masthead() {
	super();
		
	GWT.log("init Masthead()...", null);
	
    // initialise the Masthead layout container
	this.addStyleName("crm-Masthead");	
    this.setHeight(MASTHEAD_HEIGHT);
    
	// initialise the Logo image
    Img logo = new Img("logo.png", 48, 48); 
    logo.addStyleName("crm-Masthead-Logo");	  
    
	// initialise the Name label	
	Label name = new Label();  
	name.addStyleName("crm-MastHead-Name");  
	name.setContents("myCRM"); 
    
    // initialise the West layout container
    HLayout westLayout = new HLayout();
    westLayout.setHeight(MASTHEAD_HEIGHT);	
    westLayout.setWidth("50%");
    westLayout.addMember(logo);
    westLayout.addMember(name);
    
    // initialise the Signed In User label
	Label signedInUser = new Label();  
	signedInUser.addStyleName("crm-MastHead-SignedInUser");  
	signedInUser.setContents("<b>Rob Ferguson</b><br />upTick");   

    // initialise the East layout container
    HLayout eastLayout = new HLayout();
    eastLayout.setAlign(Alignment.RIGHT);  
    eastLayout.setHeight(MASTHEAD_HEIGHT);
    eastLayout.setWidth("50%");
    eastLayout.addMember(signedInUser);	
    
    // add the West and East layout containers to the Masthead layout container
	this.addMember(westLayout);  	
	this.addMember(eastLayout); 
  }	
}

and then we need to update the applications's style sheet, myCRM.css to include a background image and some padding around the logo.

...

.crm-Masthead {
  background-image: url(images/masthead.png);
  background-repeat: repeat-x; 
}

.crm-Masthead-Logo {
  /* height: 48px;
  width: 48px; */
  padding-top: 4px;
  padding-left: 8px;	
}

.crm-MastHead-Name {
  color: #FFFFFF;  
  font-family: Tahoma, Verdana, sans-serif;
  font-size: 14px;
  font-weight: bold;
  overflow: hidden;
  padding-top: 12px;
  padding-left: 2px;	
  text-align: left;
}

.crm-MastHead-SignedInUser {
  color: #FFFFFF;  
  font-family: Tahoma, Verdana, sans-serif;
  font-size: 10px;
  font-weight: normal;
  overflow: hidden;
  padding-right: 4px;
  text-align: right;
}

3. Test the application in development mode

Launch myCRM in development mode and it should now look like the following screen shot.

 

What's Next

At this point, we've built the basic UI components of myCRM by implementing smartGWT widgets and layout containers. The widgets don't respond to any input yet so next we'll wire up the widgets to listen for events and write the code that responds to those events.

Manage Events