<repository>
<id>tmlab</id>
<url>http://maven.topicmapslab.de/public</url>
</repository>
Writing User interfaces is a time consuming and repeating task. Most input masks for a model have the same layout which consists of a label, which indicates the editable property and a widget to edit the value of the property.
Most properties use a text field for editing. If the property of any type of number, the input mask might only accept digits. This behaviour has to be added to any text field of property.
Another feature of input masks is the validation of the data. Even simple validation like checking if a value was entered needs additional code, which repeats for every widget which is used.
Kuria provides an automatic way of creating UI widgets based on a given domain model.
The Kuria project is split in three parts:
kuria.runtime specifies some binding interfaces and classes to communication between the widget and a domain model property
kuria.generator provides classes to generate the UI widgets. right now only SWT is supported
kuria.annotation provides a set of annotation which can be used to configure the domain model properties fir the generator
Kuria consists of three modules which can be found in our maven repository. At the moment there is only one generator module using SWT.
To use Kuria add the following lines to you pom.xml to add the Topic maps Lab repository:
<repository>
<id>tmlab</id>
<url>http://maven.topicmapslab.de/public</url>
</repository>
After that add the Kuria SWTGenerator to you dependencies like:
<dependency>
<groupId>de.topicmapslab.kuria</groupId>
<artifactId>kuria-swtgenerator</artifactId>
<version>1.0.0</version>
<scope>compile</scope>
</dependency>
The other modules are dependencies of the SWTgenerator and will be added automatically.
SWT is a platform dependent library which needs implementations for a specific platform. To set the platform you develop, the SWT generator project provides profile specific dependencies. The following profiles are supported:
windows for Windows™ 32bit
windows_64 for Windows™ 64bit
macosx for MaxOS X 32bit (Cocoa)
macosx_64 for MaxOS X 64bit (Cocoa)
linux_x86 Linux 32 bit (GTK)
linux_x86_64 Linux 64 bit (GTK)
It is possible to install Kuria into an eclipse installation. Add the following url into the update manager:
http://kuria.googlecode.com/hg/update/
After installing the feature you can use Kuria in your Eclipse plug-in.
Kuria does not use a FormToolkit. If you want to adapt an InputMask use the following code:
private void adapt(FormToolkit toolkit, Control control) {
toolkit.adapt(control, true, true);
if (control instanceof Composite) {
for (Control c : ((Composite) control).getChildren()) {
adapt(toolkit, c);
}
}
}
The runtime library specifies the classes which are used to bind the widget with the edited model, hence these classes are called bindings.
Bindings have two tasks:
providing some configurations for the widget
provide access to the property (read/write)
Bindings are generated by implementation of IBindingFactory. The factory generates an instance of IBindungContainer which contains bindings for a set of classes. Some factories, like the AnnotationBindingFactory need some configuration, like the classes to parse.
The following chapters will explain the different bindings and their properties. For every binding an interface and an example implementations for java beans exists.
The ITextBinding specifies which value of a method or field will be used when a String representation is needed. THis may be the case for columns in table, nodes in trees or values in comboboxes. If none is specified, the value of toString is uesed.
Tablebindings can be found in the package: de.topicmapslab.kuria.runtime.table.
The ITableBinding interface specifies a container which contains a list of IColumnBinding . The binding also contains the class which is bound.
The IColumnBinding interface connects the property of the class with a column of a table. The column binding provides a title of the column, which can be used as column header. It also provides a path to an image, which will be shown left to the value of the property. In addition it provides access to the value of the property.
Treebindings can be found in the package: de.topicmapslab.kuria.runtime.tree.
The ITreeNodeBinding interface indicates that the represented object may be used as node in a tree. The list of IChildrenBinding it provides contains bindings for the children of the node.
The IChildrenBinding interface is used to indicate which properties of an element which may be used as child nodes. Every type of child has to have an ITreeNodeBinding.
For every type of widget exists a corresponding binding. Every binding is inherits from de.topicmapslab.kuria.runtime.IPropertyBinding.
The IEditableBinding interface is used to bind a property container, usually a class with the Kuria widget generators. It contains the bindings for every property of the container.
The IPropertyBinding interface provides access to the bound property. In addition it provides a label for the widget and the following flags:
read only - indicates the property must not be modified
optional - indicates the property is optional and their is not needed to be set
isArray - indicates the property is an array
is Collection - indicates the property is a java.util.Collection or of a subclass
description - a description of the field; may be used in tooltips
The IPropertyBinding provides methods to access and modify the value of the property for a specific property container.
The following bindings inherit from IPropertyBinding and therefore get its properties.
The ICheckBinding interface indicates the use of a check box for the property. Usually these properties have boolean values.
The ICheckBinding interface is used to set a property based on given choice options. This is commonly used for non primitive types. How the choices are provided is left to the widget implementation.
The IDateBinding interface is used to specify the use of a widget for Dates. It is used for the data type Date.
The IGroupBinding interface is used to embed another InputMask into the current one. This can be used if a property’s type is another property container of the domain model.
The IListBinding interface is used to specify the widget for a collection of attributes. This collection may be shown inside a table or rendered comma separated in a text field. The style is asked with the accessor getListStyle which returns a value of the enumeration ListStyle:
ListStyle.COMPACT represents the text field version
ListStyle.TABLE represents the table version
The ITextFieldBinding interface indicates that the properties value is shown in a text field. It has the following properties:
row - The number of rows in the text field. If it is greater than 1 the text field is a multiline text field with the given rows.
grabVerticalSpace - If this is true the text field will vertically expand if their is enough place it in the input mask
isPassword - Indicates if the text field should be a text field for passwords, which does not show the content of the text field.
regExp - The regular expression is an additional way to validate the content. If you use it you need to set the regular expression like it is explained in http://java.sun.com/j2se/1.5.0/docs/api/java/util/regex/Pattern.html
The Kuria annotation module provides a set of Java 5 annotations for domain models. In addition the module contains a binding factory which creates bindings for every class which is annotated. Most of the annotations are similar to the bindings.
The text annotation indicates that the field or method should be called when a String representation is needed.
public String name(String val);
Kuria has two annotations for the table bindings. The first one is @TableElement which is used at types. The binding factory creates a table binding for the type.
The other annotation is @Column. Every property of the annotated type which should be rendered inside a table column must have this annotation. Other properties are ignored.
@Column has the following parameter:
String image the path of the image to use for nodes of this type. The image must be inside the classpath.
String imageMethod instead of specifying a static image it is possible to tell Kuria which method returns the image path. This parameter contains the name of this method
String textMethod instead of using the default text it is possible to set similar to the imageMethod a method which creates the text for the table cell.
To generate a TreeNodeBinding a class must be annotated with @TreeNode. This annotation has the following parameters:
String image the path of the image to use for nodes of this type. The image must be inside the classpath.
String imageMethod instead of specifying a static image it is possible to tell Kuria which method returns the image path. This parameter contains the name of this method
To specify the attributes representing the children, the annotation @Children is used. The annotation may be used on attributes or accessor methods. The properties of this annotation are used to specify a mediator node, which may used to categorize multiple children. The properties are:
String title the text for the mediator node
String image the image of the mediator node
If the parameters are not set no mediator node is created.
|
Note
|
If a mediator node is needed the text is mandatory, the image optional. |
To create an input mask for a model, it is necessary to create an EditableBinding. The AnnotationBindingFactory creates this binding based on the @Editable annotation. If the model has an annotation, for every attribute a default property binding is generated. If an attribute should not be shown in the input mask it must be annotated with @Hidden.
The following annotations provide more configurations:
Indicates that a property is modifiable in a text field. Only attributes of primitive data types and String should be annotated with @TextField.
The Parameters are:
String label the label for the text field if not set the name of the property is used
boolean readOnly Flag to set the text field is only read only. Default: false
boolean password Flag if the text field is a password field. A password field does not show its content. Default: false;
String regexp A java regular expression for validation on the text field. Default: "\\.*";
int rows Number of rows of the text field. For multiline text field set this parameter to a value greater than 1. Default 1;
boolean grabVerticalSpace Flag to indicate if the input mask is smaller than the parent composite the text field should expand. This is only useful with multiline text fields. Default: false
boolean optional Flag which indicates that the property is optional. Default: false;
int weight A weight which is sued to sort the widgets in an input mask. Default: 1;
description A text which describes the field Default: empty string
This annotation indicates that a boolean property should be modified using a check box.
The Parameters are:
String label the label for the text field if not set the name of the property is used
boolean readOnly Flag to set the text field is only read only. Default: false
boolean optional Flag which indicates that the property is optional. Default: false;
int weight A weight which is sued to sort the widgets in an input mask. Default: 1;
description A text which describes the field Default: empty string
This annotation indicates hat the property is set via a combo box. The combo box must be filled with an array of objects of the property’s type.
The Parameters are:
String label the label for the text field if not set the name of the property is used
boolean readOnly Flag to set the text field is only read only. Default: false
boolean optional Flag which indicates that the property is optional. Default: false;
boolean createNew Indicates if a button next to the button is shown. This button opens a dialog which let you create a new instance of the type of the annotated field.
int weight A weight which is sued to sort the widgets in an input mask. Default: 1;
description A text which describes the field Default: empty string
The date annotation indicates, that the property should be edited by an calender.
The Parameters are:
String label the label for the text field if not set the name of the property is used
boolean readOnly Flag to set the text field is only read only. Default: false
boolean optional Flag which indicates that the property is optional. Default: false;
String format /** The String format, see SimpleDateFormat */ Default: "yyyy-MM-dd’T'HH:mm:ss.SSSZ"
int weight A weight which is sued to sort the widgets in an input mask. Default: 1;
boolean showTime flag to tell the UI to show the date with or without the time of the date. Default: false
description A text which describes the field Default: empty string
This annotation indicates that the field is a absolute path to a directory.
The Parameters are:
String label the label for the text field if not set the name of the property is used
boolean readOnly Flag to set the text field is only read only. Default: false
boolean optional Flag which indicates that the property is optional. Default: false;
int weight A weight which is sued to sort the widgets in an input mask. Default: 1;
description A text which describes the field Default: empty string
This annotation indicates that the field is a absolute path to a file.
The Parameters are:
String label the label for the text field if not set the name of the property is used
boolean readOnly Flag to set the text field is only read only. Default: false
boolean optional Flag which indicates that the property is optional. Default: false;
String[] fileExtensions The supported file extensions. These extensions must have the form: *.extension. For every entry in the array the file dialog provides a selection in the combo box. If you want to filter more than one extension at once use: "*.ext1;*.ext2"
int weight A weight which is sued to sort the widgets in an input mask. Default: 1;
description A text which describes the field Default: empty string
This annotation is used to embed an input mask for the property. This is used if the type of the property is another complex type. A EditableBinding for the type must exists.
The Parameters are:
String label the label for the text field if not set the name of the property is used
boolean readOnly Flag to set the text field is only read only. Default: false
boolean optional Flag which indicates that the property is optional. Default: false;
int weight A weight which is sued to sort the widgets in an input mask. Default: 1;
description A text which describes the field Default: empty string
This annotation indicates hat the property is set via a list. The list can be a compact one, which means the whole widget is one row in the input mask, or a table with buttons to remove or add new elements. Like @Combo the list provides a createNew parameter which allows the new creation of selections. Is this flag false only a set of selection options provided by the application is selectable.
The Parameters are:
String label the label for the text field if not set the name of the property is used
boolean readOnly Flag to set the text field is only read only. Default: false
boolean optional Flag which indicates that the property is optional. Default: false;
ListStyle style style of the list. Its either ListStyle.TABLE or ListStyle.COMPACT. Default: ListStyle.COMPACT
boolean createNew Indicates if a button next to the button is shown. This button opens a dialog which let you create a new instance of the type of the annotated field.
int weight A weight which is sued to sort the widgets in an input mask. Default: 1;
description A text which describes the field Default: empty string
The SWT Generator is used to generate widgets based in the bindings. These widgets are generated with the Standard Widget Toolkit (SWT) and a wrapper library called JFace. Both libraries are part of the Eclipse Project. See http://eclipse.org/swt/ for more information.
To use the generated widgets a parent widget is needed. This could be a Shell, which is the class representing a window or a Composite.
To generate one of the three main widgets instantiate the class WidgetGenerator and call the generation methods.
A TableBinding is realized using the class TableViewer. See http://www.jdocs.com/eclipse/3.2/org/eclipse/jface/viewers/TableViewer.html for more information. The table viewer uses the ColumnBinding and generates the needed content providers.
The input of the TableViewer, set with the method setInput must be a Collection of the annotated model class.
To generate a TableViewer call the method generateTable. The method exists with two, three or four parameters:
Class<?> clazz the class object which binding is used
Composite parent the parent widget, which should be a composite or shell
IContextMenuListener contextMenuListener a listener which generates actions for a context menu for the table
style the SWT style for the table widget. For more information refer to the SWT documentation
The TreeBinding is used to generate an instance of TreeViewer. Similar to the TableViewer the TreeViewer uses a ContentProvider and a LabelProvider which use the binding information.
The input of the TreeViewer is the root node of the tree.
To generate a TreeViewer call the method generateTree. Again the method exists with two or three parameters: * Composite parent the parent widget, which should be a composite or shell * boolean showRoot indicates if the root node should be visible or not * IContextMenuListener contextMenuListener a listener which generates actions for a context menu for the selected tree nodes.
|
Note
|
If you want to set another layout data to the widget you need to set it to the parent of the table widget. This is necessary, because the table is layouted using a TableColumnLayout which needs to be set into a parent of the table. |
The class InputMask represents a form for a specific class with an EditableBinding. The InputMask is a container which contains the specific widgets for the bindings. In addition the InputMask validates the input and persist the data into the given model instance.
To generate an input mask for a model type, use the method generateEditable. The method has the following parameters: * Class<?> clazz the class object which binding is used * Composite parent the parent widget, which should be a composite or shell
In addition to the input mask and viewers, Kuria provides some complex widge6ts based on the basic ones. These widgets and their use are explained in the following sections.
The MastetDetailWidget combines a TableViewer for an object with an InputMask. The table is used to display a list of model instances. By selecting one instance the input mask is filled with the instances values. The modification can be canceled or persisted in the widget.
Additionally the widget provides buttons to create new model instances or remove existing ones.
The input of the widget is a list of instances which can be modified.
The MasterDetailsWidget uses its own widget generator, so you just instantiate it. The constructor has the following parameters:
Composite parent the parent widget
int style this is a SWT flag for the style
IBindingContainer bindingContainer the binding container containing the bindings for the type of the model instance and all classes used in the class
Class<T> clazz the class which is annotated and the type of the model instances
boolean horizontal a flag whether the table is layouted on the left side or on top of the input mask this parameter is optional. The default is false.
Kuria provides a few new listener interfaces, which can be used to react on changes.
Some applications need to persist their model on their own, for instance when using a undo/redo history. Kuria provides a listener interface which is called when model changes happen. The IModelListener uses PerstenzEvent*s which have a flag (*commit) to indicate whether Kuria should persist the new values into the model or not. The latter is should be used if the values are set in another part of the application.
See the javadoc for more information.
An instance of a listener is invoked if a context menu is created for a {@link TableViewer} or {@link TreeViewer}. The listener has one method: createMenu(IMenuManager manager).
The implementation of the should get the selection of the viewer and add the provided actions to the manager.
With the IInputMaskListener it is possible to react on changes in the input mask. The methods are:
dirtyChanged Is called when the dirty state of the input masks changes. The dirty state indicates if the values of the edited model were changed.
newModelElement(Object newElement) Some widgets provide a way to create a new model element and set it as value for a property. Examples are lists or combo boxes. If a new element was created this method is called, so the application can persist the new created model instance on its own way.
Every input mask may have a selection list or combo box. These widgets need a list of values to choose from, which Kuria cannot know. To set these selectable values, the input masks uses implementations of IContentProvider. Every input masks has one provider, so if more than one field needs provided elements, the provider must choose which elements to return based on the field name and the model instance.
The methods are:
Object[] getElements(String fieldname, Object model) Returns the possible values for the field name.
boolean hasContent(String fieldname, Object model) Checks if the provider provides content for the given field and instance.
Kuria tries to load images from the classpath using the path of the binding. It may happen that the image is an external image and Kuria cannot find it. Is this the case the application may register an implementation of this interface and load the image on its own.
Image loadImage(String path) This method is called if loading the image of the given path failed. The implementation may use different approaches to load the image.
A callback implementation can be registered in an instance of WidgetGenerator.
|
Note
|
The callback is added to an internal image registry. The image registry is a singleton and every callback must be removed explicitly when it should not be used anymore. |
Labels for Columns or widgets in input masks will be derived by:
Annotationproperties like title or label
Name of the field in a class
To provide a way of internalization a ILabelProvider implementation can be added to a IBindingContainer. The method getLabel retrieves a String which is the label retrieved by annotation or fieldname and returns either the same string or a translation of the String.
A binding connects the ediablt property of the domain model with the UI element. It provides access to the properties and the configuration for the UI element.
A node is an element of a tree. Every node may have children which are nodes itself. A tree starts with a root node, which is a node without a parent node.
A widget is visual element in user interfaces. Widgets are e.g. Buttons, Labels and text field .
Generated interfaces for bindings, makes it possible to create new bindings for other model types than java beans
Added validation flags and accessors
Binding classes for every annotation/widget
contains generic factory creating widget bindings for classes based on property type
created set of annotations for the common widgets
annotation factory which falls back to generic factory
Added optional flag to widget annotations
Uses valid flags of bindings to check if persist may be called or not
UI reports errors if validation fails: decorator for widgets
generates SWT widgets based on bindings
provides new widget container called InputMask
State of model can be observed with state listeners
widget data is not automatically stored in domain model generate tree and table viewer (JFace)