Spark Skinnable SVG GUIs Investigation - Draft
Overview
Goals
The primary goal of this investigation was to create a Hello World SVG application that could have multiple skins applied to it without requiring any changes to the application SVG or scripts. It was felt that creating an example would be more fruitful than continuing to research the multitude of options available.
Notation
The follow stylings are used in an attempt to improve clarity:
- xml attributes
- the className of a class attribute
- the idValue of an id attribute
Caveats
- The script code in the example is ugly. It's definitely investigational/experimental code. It contains dead ends and many commented out chunks. It's nowhere near bullet-proof.
- The demo is buggy. I recently introduced a bug in the initialization code that leaves the viewer in a bad state. Usually the applications works correctly but hitting the browser's Refresh button sometimes causes crashes.
- The class implementations don't share any code but should (there's definitely redundancy). The coding conventions discussed (e.g. spaces instead of tabs) were not followed; etc.
- The CGUI code was only leveraged indirectly as a reference. A proper implementation of the ideas I've been exploring will reuse the CGUI class hierarchy and much of the code.
- This has only been tested under Win2000, IE6, ASV
Files Used
Roles
- app.svg
- Contains application specific SVG
- Contains description of the UI resources
- app.js - application specific script and callbacks following naming conventions (describe below)
- skin.svg
- svg definitions of the skin
- template for each widget
- skin.js
- implementations of the widget classes
- UI initialization code
In the demo provided, the file index.html shows three instantiations of the same application. The same .js files are used for each. A copy of the same app.svg file is in each of the two folders svg and secondskin. The only file that differs is the skin.svg file in each folder. The third application is just a styling of the first application.The version of app.svg in the styledskin folder differs only in that it includes a style sheet.
Use of the class attribute
The class attribute is used in two distinct manners with the usage context differentiating the two cases. First, it's used to indicate the type of a widget (e.g. checkBox) when defining the UI resources. Secondly, it's used to provide the parameter names when describing the components of a given widget (e.g. message).
Use of the id attribute
The id attribute is used to provide unique identifiers for the UI elements. Since we're guaranteed that the id's are unique, they provide the foundation for a naming convention for callbacks. Widget callbacks take the form idfoo(params, ...). The application developer is responsible for implementing these callbacks. For example,
- for the resource <g class="checkBox" id="fillCheckBox"> the callback is fillCheckBoxChange(newValue), where newValue is a boolean indicating the new checked state. This is called whenever the check box's state changes.
- for the resource <g class="radioButtonGroup" id="fillButtonGroup"> the callback is fillButtonGroupSelect(radioId), where radioId is the id of one of the group's buttons. This is called whenever one of the groups button is selected.
These naming conventions allow for the UI initialization code to be shared amongst applications.
The application developer may then author his scripts using the id values much like traditional resource ids.
Key File Details
app.svg
- The onload attribute in the top level <svg> element is used to start the initialization process.
- Two things are present to force the skin loading:
- The application group has the attribute externalResourcesRequired set to true.
- The skin is included as an invisible image: <image xlink:href="skin.svg" width="0" height="0" visibility="hidden"/>
- Note: this was performed because the initialization code needs access to the skin.svg file. However, the getURL() call is asynchronous and the skin file is required prior to the initial renderiing... (better ideas are welcome!)
- The uiResources group provides a parameterization of the UI. This parameterization includes:
- the initial position of the widget via the transform attribute
- the type of the widget via the class attribute
- the unique identifier for the widget via the id attribute
- specific parameters such as message (which provides the label for a button) and value (which provides the initial state of the check box).
skin.svg
- Provides definitions for the geometry of the widgets.
- Also provides definitions of patterns used by the skin.js code to alter the states of the widgets (e.g. selecting and de-selecting the check box). Performing these variations via patterns allowed the same script to be used for different skins.
- store the instantiated Spark elements within this object - map?
skin.js
- Contains the implementations of the generic widget objects.
- Contains initialization code that walks through the uiResources elements and replaces the parameters with the definitions from skin.svg
- Builds a list of instantiated widgets that can later be retrieved by id. This is needed so that the application scripta can look-up and show/hide controls; also controls may need to locate others (e.g. to disable radio buttons)
Areas For Improvement
Non-conforming (or semi-conforming) skins
- Need to override non-comforming aspects
- e.g. for a check box to indicate its state by something other than the fill attribute of the box element
- the current implementation toggles the fill between the checkOn and checkOff patterns
- A different implementation would just need to override the CheckBox.prototype.setState member.