Generic Widget Functionality
- Caleb Burch (Unlicensed)
- andrew johnston (Unlicensed)
- Christopher Morgan (Unlicensed)
- AlexI (Unlicensed)
Overview
This section covers functionality and features that are not platform specific. eg. look and feel, navigation, drag & drop, styling, widget help etc.
For a tutorial that covers widget functionality that interacts with the json query API, see Writing your first Widget.
In this section:
Widget Header
All widgets have a header that developers have direct access to for adding content. There are currently 2 version of the header which work as follows below. To use them just include the extra mxml tags shown in your widgets main mxml page.
Traditional Way (not recommended): This is the way all legacy widgets have used. It gives you access to a single header bar to put anything you want, and will continue expanding until the widget width is met, at which point all additional content is cut off.
Header bar: <components:headerContent> <components:WidgetIgnoreFilterToggleButton id="localFilterSettings" toolTip="Ignore Workspace Filtering - Show All Results" click="setTimeout( onClickIgnoreLocalFilter, 100 )" /> </components:headerContent>
New/Better Way (recommended): This is the way all future widgets will be developed. It gives you access to three header bars to put anything you want. The first bar is a Left Aligned section that always will show up next to the title of the widget. The next is the same header bar as the traditional that will continue to fill the area remaining in the top header bar, but will still cut off if the widget width is too small. The final bar is a second level dropdown that if included will add a button to the top level header bar, and give you a second bar that has scrolling capabilities to fit as many header elements as necessary.
Left aligned header bar: <widgetComponents:leftHeaderContent> <widgetComponents:WidgetDropDownList id="caseList" includeInLayout="false" width="150" change="caseList_changeHandler(event)" prompt="Case" visible="false" /> </widgetComponents:leftHeaderContent> Second header bar: Add this property to your widget: secondToolbarButtonVisible="true" Then add this array of your components <widgetComponents:secondHeaderContent> <widgetComponents:WidgetToggleButton id="centerButton" label="Center" toolTip="Center selected nodes, or entire graph" click="centerButton_clickHandler(event)" /> </widgetComponents:secondHeaderContent>
Widget Help
If you want to add a custom help message to your widget that will display when a user clicks the '?' button on the right side of your widget, you just need to put whatever ui component you desire inside a helpContent tag as shown below.
<components:helpContent> <s:VGroup width="100%" height="100%" horizontalAlign="center" gap="5" paddingTop="5" paddingBottom="5" paddingLeft="10" paddingRight="10" > <s:RichEditableText editable="false" multiline="true" maxWidth="300" > <s:textFlow> <s:TextFlow> <s:p> This widget does xyz. More details on using this widget can be found <s:a href="http://mywebsite.com/help">here</s:a> </s:p> </s:TextFlow> </s:textFlow> </s:RichEditableText> </s:VGroup> </components:helpContent>
Styling
To ensure a similar experience between browsers, operating systems, and other environment factors we have included a default font for widgets. Because of the way widgets are dynamically loaded modules, if you want to take advantage of the global font widgets, you must include our css stylesheet. If you use the eclipse plugin or predefined project in the getting started section, these are created for you and included in your widget automatically. If you choose not to use the Infinit.e font you are more than welcome to unreference the stylesheet and go with your own approach.
The stylesheet included with your project is just an example and is not uploaded with your project when it is being submitted. These changes will not be reflected when uploading your widget to the main application.
In the stylesheet we have included styles, all spark components, and a selection of common MX components. If you include a MX component that we have not styled in our stylesheet, you have the option to add a style to your widget individually. If using the eclipse plugin or predefined project there is a commented out example in the <fx:Style> tag of adding the font to a mx:Text component.
Here is an example that also achieves the same results:
<fx:Style> mx|Text { font-family: infiniteNonCFFFont; } </fx:Style>
Widget Saving - Community vs. User Options
Typically a widget will save any options that it wants on reloading during a onSaveWidgetOptions call. Any JSON object passed to the widget framework on that call will be returned when the widget is loaded via onLoadWidgetOptions in a WidgetSaveObject. The WidgetSaveObject has two interesting fields, userSave
and communitySave.
The userSave
object is the standard object that was saved during a onSaveWidgetOptions call and should be used for any save options that are unique to an individual user. Examples of these save options include a user's current zoom level and centerpoint on a map widget, and the selected graph type for a statistics widget.
//Here is an example of creating an anonymous object (tempWidgetOptions) //and setting some fields to save the zoom and centerpoint of a map widget //This method will be polled every few minutes public function onSaveWidgetOptions():Object { var tempWidgetOptions:Object = new Object(); tempWidgetOptions[ "centerLat" ] = _map.center.lat; tempWidgetOptions[ "centerLng" ] = _map.center.lng; tempWidgetOptions[ "zoomLevel" ] = _map.zoom; return tempWidgetOptions; } //The next time a widget is opened it will be sent the anonymous object //that was last saved from onSaveWidgetOptions //that object can be used to restore previous states public function onLoadWidgetOptions( widgetOptions:WidgetSaveObject ):void { if ( widgetOptions != null ) { //this holds a users last map if ( widgetOptions.userSave != null ) { this.widgetOptions = widgetOptions.userSave; //widgetOptions contains fields centerLat,centerLng,zoomLevel from our last save } } }
communitySave
The communitySave object is used for giving a widget specific saved data for a given community. Example of sample uses include the following
- giving an intelligence community a KML-specific view to a region of interest on a map widget,
- using a certain color scheme specific to business operations for a statistics widget.
The communitySave objects can only be set external to a widget in http://infinite.ikanow.com/manager/fileUploader.jsp
To create your own communitySave
- Create a JSON file with the data you want to pass in (see KML example below for the built in map's kml layer adder)
- Upload the JSON file to a new JSON share in the fileUploader.jsp with the following fields set:
- Title: Must match the widget you want the communitySave to be accessible in. For example, if you want a KML layer in the Map widget you must name your share "Map"
- Type: Must be "widgetsave"
- Community: Must be a non-personal community (e.g. select a community you own)
Any JSON saved in this manner will be available to users in that community in the onLoadWidgetOptions as seen below:
public function onLoadWidgetOptions( widgetOptions:WidgetSaveObject ):void { if ( widgetOptions != null ) { //this holds a users last map if ( widgetOptions.userSave != null ) { //...code from above here } if ( widgetOptions.communitySave != null ) { //Any shares you have access to with type widgetSave will be provided here //Here we will be given a map of community ids to shares e.g. { "commid12345": {"key1":"value1"}, "commid67890":{"anotherkey",["blue","green","red"]}} for ( var commid:String in widgetOptions.communitySave ) { //this is the object saved in the share for community <commid> //Now you can do what you want with it (we printed it out) var community_save_object = widgetOptions.communitySave[commid]; for ( var key:String in community_save_object ) { trace("CommId: " + commid + " key: " + key + " value: " + community_save_object[key]); } } } } }
KML Layers example:
The map widget that ships with the platform shows an example of using the per-community settings in order to allow different KML layers.
To use the per-community settings for different KML layers
- Upload each KML as a binary share (not necessary if the KML is accessible via URI)
- Upload a JSON share with type "widgetsave" that looks like:
{ "kml-name1": "URI1", "kml-name2": "URI2", //etc }
Referencing items in JSON shares:
Any item uploaded to the fileManager.jsp can be referenced via "$infinite/share/get/{id}". This is useful when clients may use different dns names to access the gui.
If you are writing your own widget, the expansion should replicate:
var ENDPOINT_URL:String = flash.external.ExternalInterface.call( "getEndPointUrl" ); url = url.replace( "\$infinite/", ENDPOINT_URL );
Widget Drag and Drop
A generic drag and drop interface is implemented for sending documents, entities, and associations between widgets. To accept these things a widget only needs to implement an event handler for the widgetDrop event on WidgetModule.
<components:WidgetModule xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:components="com.ikanow.infinit.e.widget.library.components.*" xmlns:s="library://ns.adobe.com/flex/spark" xmlns:mx="library://ns.adobe.com/flex/mx" implements="com.ikanow.infinit.e.widget.library.widget.IWidget" widgetDrop="widgetmodule1_widgetDropHandler(event)"> <fx:Script> <![CDATA[ import com.ikanow.infinit.e.widget.library.events.WidgetDropEvent; protected function widgetmodule1_widgetDropHandler(event:WidgetDropEvent):void { trace("Ents: " + event.entities.length); trace("Assocs: " + event.associations.length); trace("Docs: " + event.documents.length); trace("Source: " + event.dragSource); trace("WidgetName: " + event.dragWidgetName); trace("WidgetClass: " + event.dragWidgetClass); } ]]> </fx:Script> </components:WidgetModule>
The WidgetDropEvent object holds 3 anonymous arrays: entities,associations,documents, and any/all of these may have data in them.
The object also holds some information on where the drag came from:
dragSource: the dragger manually specifies this, can be anything
dragWidgetName: the title of the widget the drag came from
dragWidgetClass: the class of the widget the drag came from (i.e. the mxml file name of WidgetModule)
To send dragged items to another widget,
- dispatch an drag event with the dataformat using WidgetDragUtil.WIDGET_DRAG_FORMAT:
protected function widgetheaderdragimage1_mouseDownHandler(event:MouseEvent):void { var docs:Array = new Array(); for each ( var doc:Object in docList.selectedItems ) { docs.push(doc); } var dragObject:WidgetDragObject = new WidgetDragObject(); dragObject.documents = new ArrayCollection(docs); dragObject.dragSource = "MyCustomWidgetName"; //this is the user made drag name, can be anything var ds:DragSource = new DragSource(); ds.addData(dragObject, WidgetDragUtil.WIDGET_DRAG_FORMAT ); DragManager.doDrag(dragImage, ds, event); }
Widget Header icon
A widget header icon has been supplied in the widget library to allow uniformity between widgets.
To add the icon
- Add the header block on all widgets
- Implement the mousedown handler as shown above, supplying your specific data):
<components:WidgetHeaderDragImage id="dragImage" mouseDown="widgetheaderdragimage1_mouseDownHandler(event)" toolTip="Drag this to another widget/the query bar to send selected documents" />
The added widget header icon will look like the following: