Let us take a look into how custom GWT widgets are created. The authoritative sources for developing with GWT are the Google Web Toolkit Developer Guide and Google Web Toolkit Class Reference.
Google Web Toolkit offers a variety of ways for creating custom widgets. The easiest way is to create composite widgets by grouping existing basic widgets and adding some interaction logic to them. You can also develop widgets using the lower-level Java interfaces used by the standard GWT widgets or the really low-level JavaScript interfaces.
A custom GWT widget needs to find its place in the GWT class hierarchy. Figure 8.2, “GWT Widget Base Class Hierarchy” below illustrates the abstract base classes for GWT widgets.
Each of the base classes offers various services for different types of
widgets. Many custom widgets, such as the Color Picker example below, extend
the Composite
class to combine the widget from existing
GWT widgets. Other base classes offer various features useful for different
kinds of widgets. You can also opt to extend an existing GWT widget, as we
have done for most of the standard user interface components of IT Mill
Toolkit, or extend an IT Mill Toolkit widget.
Extending an IT Mill Toolkit widget is an easy way to add features, such
as advanced client-side validation, to existing standard components. If
the extended widget does not require any additional parameters, which is
usual in client-side validation, you may not even need to define a
server-side counterpart for your widget. A server-side component can be
mapped to multiple client-side components depending on its parameters.
The mapping is defined in the widget factory, i.e., the class inheriting
DefaultWidgetSet
. For details on how to implement a
widget factory, see Section 8.4, “Defining a Widget Set”.
In the following example, we implement a composite GWT widget built from
HorizontalPanel
, Grid
,
Button
, and Label
widgets. This widget does not yet have any integration with the server
side code, which will be shown later in this chapter. The source code is
available in the source folder for the demo application in IT Mill Toolkit
installation folder, under package
com.itmill.toolkit.demo.colorpicker.
package com.itmill.toolkit.demo.colorpicker.gwt.client.ui; import com.google.gwt.user.client.DOM; import com.google.gwt.user.client.Element; import com.google.gwt.user.client.ui.*; /** * A regular GWT component without integration with IT Mill Toolkit. **/ public class GwtColorPicker extends Composite implements ClickListener { /** Currently selected color name to give client-side feedback to the user. */ protected Label currentcolor = new Label(); public GwtColorPicker() { // Create a 4x4 grid of buttons with names for 16 colors Grid grid = new Grid(4,4); String[] colors = new String[] {"aqua", "black", "blue", "fuchsia", "gray", "green", "lime", "maroon", "navy", "olive", "purple", "red", "silver", "teal", "white", "yellow"}; int colornum = 0; for (int i=0; i<4; i++) for (int j=0; j<4; j++, colornum++) { // Create a button for each color Button button = new Button(colors[colornum]); button.addClickListener(this); // Put the button in the Grid layout grid.setWidget(i, j, button); // Set the button background colors. DOM.setStyleAttribute(button.getElement(), "background", colors[colornum]); // For dark colors, the button label must be in white. if ("black navy maroon blue purple".indexOf(colors[colornum]) != -1) DOM.setStyleAttribute(button.getElement(), "color", "white"); } // Create a panel with the color grid and currently selected color indicator HorizontalPanel panel = new HorizontalPanel(); panel.add(grid); panel.add(currentcolor); // Set the class of the color selection feedback box to allow CSS styling. // We need to obtain the DOM element for the current color label. // This assumes that the <td> element of the HorizontalPanel is // the parent of the label element. Notice that the element has no parent // before the widget has been added to the horizontal panel. Element panelcell = DOM.getParent(currentcolor.getElement()); DOM.setElementProperty(panelcell, "className", "colorpicker-currentcolorbox"); // Set initial color. This will be overridden with the value read from server. setColor("white"); // Composite GWT widgets must call initWidget(). initWidget(panel); } /** Handles click on a color button. */ public void onClick(Widget sender) { // Use the button label as the color name to set setColor(((Button) sender).getText()); } /** Sets the currently selected color. */ public void setColor(String newcolor) { // Give client-side feedback by changing the color name in the label currentcolor.setText(newcolor); // Obtain the DOM elements. This assumes that the <td> element // of the HorizontalPanel is the parent of the label element. Element nameelement = currentcolor.getElement(); Element cell = DOM.getParent(nameelement); // Give feedback by changing the background color DOM.setStyleAttribute(cell, "background", newcolor); DOM.setStyleAttribute(nameelement, "background", newcolor); if ("black navy maroon blue purple".indexOf(newcolor) != -1) DOM.setStyleAttribute(nameelement, "color", "white"); else DOM.setStyleAttribute(nameelement, "color", "black"); } }
This example demonstrates one reason for making a custom widget: it provides client-side feedback to the user in a way that would not be possible or at least practical from server-side code. Server-side code can only select a static CSS style or a theme, while on the client-side we can manipulate styles of HTML elements very flexibly. Notice that manipulation of the DOM tree depends somewhat on the browser. In this example, the manipulation should be rather compatible, but in some cases there could be problems. Standard GWT and IT Mill Toolkit widgets handle many of such compatibility issues, but when doing such low-level operations as DOM manipulation, you may need to consider them.
The structure of the DOM tree depends on how GWT renders its widgets for a
specific browser. It is also not guaranteed that the rendering does not
change in future releases of GWT. You should therefore make as few
assumptions regarding the DOM structure as possible. Unfortunately, GWT
does not provide a way to set the style of, for example, cells of layout
elements. The above example therefore assumes that the
Grid
is a table and the
<button>
elements are inside
<td>
elements of the table. See Section 8.2.3, “Styling GWT Widgets” below for more details on compatibility.
The widget will look as follows:
As you can notice, the widget will look rather uninviting without CSS styling. We will next look how to define a default style for a GWT widget.
GWT renders its widgets in the DOM tree of the web browser as HTML
elements. Therefore, their style can be defined with Cascading Style
Sheets (CSS) just as in HTML. GWT Compiler supports packaging style sheets
from the source package tree. The style sheet is defined in the
.gwt.xml
descriptor file (see Section 8.4.1, “GWT Module Descriptor” for details).
<!-- WidgetSet default theme --> <stylesheet src="colorpicker/styles.css"/>
The style sheet path is relative to the public
folder
under the folder containing the .gwt.xml
file.
Let us define the colorpicker/styles.css
as follows.
/* Set style for the color picker table. This assumes that the Grid layout is rendered as a HTML <table>.*/ table.example-colorpicker { border-collapse: collapse; border: 0px; } /* Set color picker button style. This does not make assumptions about the HTML element tree as it only uses the class attributes of the elements.*/ .example-colorpicker .gwt-Button { height: 60px; width: 60px; border: none; padding: 0px; } /* Set style for the right-hand box that shows the currently selected color. While this may work for other implementations of the HorizontalPanel as well, it somewhat assumes that the layout is rendered as a table where cells are <td> elements. */ .colorpicker-currentcolorbox { width: 240px; text-align: center; vertical-align: middle !important; /* Must be !important to override GWT styling. */ }
The stylesheet makes some assumptions regarding the HTML element
structure. First, it assumes that the Grid
layout
is a table. Second, the custom class name,
colorpicker-currentcolorbox
, of the right-hand
HorizontalPanel
cell was inserted in the DOM
representation of the widget in the GwtColorPicker implementation. Styling
a button makes less assumptions. Using only class names instead of
specific element names may make a stylesheet more compatible if the HTML
representation is different in different browsers or changes in the future.