RAP/Incubator/DropDown

From Eclipsepedia

< RAP
Jump to: navigation, search

The DropDown incubator project aims to provide a simple and efficient text input assist for RAP, also known as "type ahead", "auto suggest", "auto complete" or "field assist" in various frameworks. The current git master is only compatible to RAP master, but there is also a RAP 2.1 compatible branch that has the same features and API. P2 repositories for both versions can be found here.

Contents

The DropDown Widget

The org.eclipse.rap.addons.dropdown.DropDown widget can be found in the org.eclipse.rap.addons.dropdown bundle and is basically a free-floating List (including setItems and Selection event support) that can be attached to a Text widget or any other control. When made visible (DropDown.show()), the DropDown will be placed below the widget. It will hide automatically if the parent (Text) looses focus, ESC is pressed or an item in the DropDown is clicked. While it is visible (and the parent is focused), the selection can be changed using the "Up" and "Down" keys. This is the full extend of its functionality.

AutoSuggest

The org.eclipse.rap.addons.autosuggest.AutoSuggest component aims to provide a complete solution for text input assistance. It wraps the DropDown widget and requires only the Text widget and a data source to work properly. It can be found in the org.eclipse.rap.addons.autosuggest bundle, which has a dependency to the RAP/ClientScripting project if you use the RAP 2.1 version. (It has no special dependencies in RAP 2.2+ since these RAP versions already include ClientScripting.)

DataSource

A minimal setup of a AutoSuggest requires an instance of DataSource, which in turn requires a DataProvider.

Example:

   Text text = new Text( parent, SWT.BORDER );
   DataSource dataSource = new DataSource();
   dataSource.setDataProvider( new ArrayDataProvider( "text1", "text2", "text3" ) );
   AutoSuggest autoSuggest = new AutoSuggest( text );
   autoSuggest.setDataSource( dataSource );

The AutoSuggest will be disposed automatically with the Text widget. Please be aware that DataSources are not disposed automatically and live on the client as long as the session does or until they are disposed maually. Equal DataSources should be re-used instead of re-created to not waste client memory or create unncecessary traffic.

Templates

Unless set up differently, the suggestions that are presented to the user are simple strings that are identical to the string that will be inserted when a suggestion is selected. To allow different forms of presentation a template can be set on the data source. Currently the only available template is ColumnTemplate, which allows prestenting the suggestions as table with multiple columns. When using ColumnTemplate a matching data provider needs to be implemented, ColumnDataProvider. This provider allows defining a column text for each suggestion (getTexts), as well as the text that is actually inserted (getValue).

Example:

   Text text = new Text( parent, SWT.BORDER );
   DataSource dataSource = new DataSource();
   dataSource.setTemplate( new ColumnTemplate( 50, 150, 150 ) ); // the column widths
   ColumnDataProvider dataProvider = new ColumnDataProvider() {
     public Iterable<?> getSuggestions() {
       return Arrays.asList( someArray ); // for example an array of string arrays (String[][])
     }
     public String getValue( Object element ) {
       String[] value = ( String[] )element;
       return value[ 0 ];
     }
     public String[] getTexts( Object element ) {
       String[] value = ( String[] )element;
       return new String[] { value[ 0 ], value[ 2 ], value[ 3 ] };
     }
   } );
   dataSource.setDataProvider( dataProvider );
   AutoSuggest autoSuggest = new AutoSuggest( text );
   autoSuggest.setDataSource( dataSource );


Adanced Customization

The AutoSuggest class can be extended to modify it's behavior and/or add new features/API. This is not 100% future-proof as some of the JavaScript API that is used is considered internal, but there are currently no fundamental changes planned for Autosuggest/DropDown. (This may change, especially if these Components are migrated from the Incubator to RAP core.)

Attaching a Custom a JavaScript File

To make any signficant changes to AutoSuggest it is necessary to load your own JavaScript implementation of the AutoSuggest logic. To do so, extend the AutoSuggest class like this:

public class CustomAutoSuggest extends AutoSuggest {

  public CustomAutoSuggest( Text text ) {
    super( text );
  }

  @Override
  protected ClientListener getAutoSuggestListener() {
    return CustomAutoSuggestClientListener.getInstance();
  }

}

Copy the files ResourceLoaderUtil.java and AutoSuggest.js from the org.eclipse.rap.addons.autosuggest source bundle to an internal package that is accessible by CustomAutoSuggest. Rename AutoSuggest.js to CustomAutoSuggest.js. Then add the implementation for CustomAutoSuggestClientListener to the same package:

public class CustomAutoSuggestClientListener extends ClientListener {

  public static CustomAutoSuggestClientListener getInstance() {
    return SingletonUtil.getSessionInstance( CustomAutoSuggestClientListener.class );
  }

  private CustomAutoSuggestClientListener() {
    super( getText() );
  }

  private static String getText() {
    String path = "path/to/your/CustomAutoSuggest.js";
    return ResourceLoaderUtil.readTextContent( path );
  }

}

Now you should be able to use CustomAutoSuggest like the normal AutoSuggest class.

Understanding and Customizing AutoSuggest.js

The AutoSuggest instance is represented on the client by a simple model object (API is below), however this is not what AutoSuggest.js is. AutoSuggest.js contains the client logic that manages the interaction between DropDown, Text and the data model. It's handleEvent function gets all important events from the client representations of DropDown, Text and Autosuggest(model) and delegates them. First, all important properties of Text and DropDown are synchronized with the model in the "sync"/"forward" functions. Usually you don't have to touch those. The logic itself is implemented within the functions starting with "on", which are called whenever a significant event (usually a value change) occurs on the model. They all have one parameter (the event object) and have the model object as their context ("this").

The model is a simple container of key/value pairs that fires a change event whenever a value is modified. The implementation can be found in Model.js in the AutoSuggest bundle. The API consists of the following methods:

set( property, value [, options ] ) options may be any object and is attached to the change event
get( property ) returns the value for this property, or undefined is none is set
notify( eventType [, eventProperties ] ) eventType may be any string. eventProperties may be any object given to the listeners as the first (and only) argument


The following properties are used on the AutoSuggest model:

property type set by description
autoComplete boolean server enables autocomplete behavior
dataSourceId string server id of yet another model that contains the suggestions (see DataSource.java)
autoSuggestListenerId string server id of the handleEvent function of AutoSuggest.js
textWidgetId string server id of the text widget
dropDownWidgetId string server id of the dropDown widget
text string client text currently set on the text widget
userText string client text last entered by the user
suggestionsVisible boolean client visibility of the dropDown widget
suggestions array of any type (e.g. string) client (server via DataSource) all available suggestions provided by DataSource
filter function client (server via DataSource) function that returns boolean for call "filter( suggestion, userText );"
template function client (server via DataSource, not implemented yet) function that returns string to be displayed by dropDown for a given suggestion
currentSuggestions array of any type (e.g. string) client suggestions matching current userText
currentSuggestionTexts array of string client currentSuggestions as displayed by dropDown
selectedSuggestionIndex number client index of the user selected suggestion in the currentSuggestions array
replacementText string client text suggested to be entered into text widget
textSelection array of number ( [ start, length ] ) client text to be marked as selected (inserted) - synced only from model->text, not back


In case the ColumnDataProvider is used on the server side, a suggestion will be an array of strings (with the first used as the replacementText and the rest as the columns in dropDown), otherwise it's just a string.

The following events are used on the AutoSuggest model:

type fired by description
change set call (server or client) event object described below
accept client user wants to accept a suggestion (equals defaultSelection on dropDown caused by click or enter)
suggestionSelected client successfully accepted an suggestion (this is what the server listens to)


A change event object fired by a "set" call on the model has the following fields:

value the new value
type usually "change"
property name of the property that changed
options the options object given as the third parameter on the "set" call, see below
source the object that fired the event, i.e. the model


When a property changes, an options object is given in the change event, which currently may have one property "action" that indicates what caused the change.

action properties description
sync text, suggestionsVisible, selectedSuggestionIndex, replacementText value synchronized from widget to model (except replacementText)
typing userText, currentSuggestions changed due to user typing text
deleting userText, currentSuggestions changed due to user deleting text
refresh currentSuggestions changed due to changed DataSource
selection replacementText changed due to changed selectedSuggestionIndex (user selects suggestion)

(replacementText giving an action "sync" is not quite semantically correct as it's more an internal reset, may be changed.)

Adding new properties to AutoSuggest

(RAP 2.2+ version only.) To enable the application developer to customize the AutoSuggest him/herself, it is possible to add public setter. Such a setter has to call set on the remoteObject to forward the property to the client-side AutoSuggest model:

  public void setEagerAutoComplete( boolean value ) {
    remoteObject.set( "eagerAutoComplete", value );
  }

On the model, this property is now available without any further changes:

  if( this.get( "eagerAutoComplete" ) ) {

Note that the default value of a property that has not been set on the model is undefined. However, since undefined is falsy in JavaScript it can be used like a boolean that is false.

Accessing Text and DropDown client widgets

Here are some further information, in case you need to use properties or events that are not yet forwarded to the model, or need to access the widgets directly for some reason.

The client Text widget API is documented here: http://download.eclipse.org/rt/rap/doc/2.2/guide/reference/jsdoc/symbols/Text.html
The client DropDown widget API consists of the following methods:

setItems( items ) expects an array of string. When columns are used, the cells are separated by tabs.
getItemCount() returns number
setSelectionIndex( index ) expects an number between 0 and items.length - 1
getSelectionIndex() returns number
setVisible( value ) expects boolean
getVisible() returns boolean
show() same as setVisible( true )
hide() same as setVisible( true )
setData( key, value ) attach any value to the widget
getData( key ) returns data attached in a script or by the server


DropDown and Text fire events according to http://download.eclipse.org/rt/rap/doc/2.2/guide/reference/jsdoc/symbols/Event.html
Currently, the following events are forwarded from DropDown to the AutoSuggest.js handleEvent function:
SWT.Show, SWT.Hide, SWT.Selection, SWT.DefaultSelection
These are all events available on DropDown.

By default, the following events are forwarded from Text to the AutoSuggest.js handleEvent function:
SWT.Modify, SWT.Verify
It is possible (RAP 2.2+ version only) to add more events to that list by overwriting getTextEventTypes in AutoSuggest.java:

  @Override
  protected int[] getTextEventTypes() {
    return new int[]{ SWT.Modify, SWT.Verify, SWT.MouseDown };
  }

Unit Tests

There is a jasmine test suite in the AutoSuggest test bundle. It can be used with the modified AutoSuggest.js to prevent breaking existing functionality. However, you will have to adjust most paths in the index.html to point to your location of the org.eclipse.rap.jstestrunner, org.eclipse.rap.addons.autosuggest and org.eclipse.rap.rwt bundles The first argument of "TestUtil.loadResource( "AutoSuggest.js", listenerPrefix + "AutoSuggest.js" );" needs to remain unchanged, while the second is the relative path to your CustomAutoSuggest.js. The index.html has to be loaded from an http server.