|
The following tutorial will give you an overview over the main features of QXML, the XML schema used by QxTransformer. To learn how to download and install the library itself, please see here. Remember that you need to run, from the frontend directory, make transform and then "make build" or "make source" (depending whether you are working with a build or source version of your application) after each change to the XML code so that the JavaScript code is updated. QXML Syntax QxTransformer takes up the syntax of the QxBuilder class that was
present in the qooxdoo 0.1 - 0.5 releases, but extends it
significantly. Currently, It uses two namespaces, “qx” for “real” qooxdoo
widgets and “qxt” for “virtual” widgets constructed by templates. We call the resulting XML code "QXML" (for qooxdoo XML).
To avoid lengthy namespaces (and typos), xml tag names do not use the
full namespace of the classes, but use an abbreviated name similar to
the class names in qooxdoo 0.1. A list of <qx:...> tags can be found here, a list of <qxt:...> tags will be forthcoming, we will cover the most important ones in this tutorial. We will also provide an XSD schema file in one of the next releases, which will be very useful when writing QXML documents. At the moment, only the namespaces "qx" and "qxt" are supported. In a coming release, we will support custom namespaces, so that you can call your custom widgets using your application namespace. Each node corresponds to an anonymous variable name created by the XSLT engine. For example, <qx:atom/> is translated into “var qx_1337463242 = new qx.ui.basic.Atom()”. If you want to have a named variable reference to your newly created object, you need to specify an id, such as <qx:atom id=”foo”/>. If this reference should be a global variable, you can provide the “scope” attribute and set it to “global”. <qx:atom
As you can see, all properties that you want set on this widget are specified as XML attributes. For example, myFirstAtom = new qx.ui.basic.Atom("Hello World","icons/16/smiley.png");
Note that QxTransformer automatically knows which attributes are used as constructor variables. All others get set seperately. An "id" attribute is not necessary, but useful if you want to refer to this widget later by variable name. If you add 'scope="global"', the id will be present in the global namespace.
As can be expected, the widget object hierarchy is modeled as parent-child node relations. For example, a list contained in a horizontal box layout is constructed like this: <qx:horizontalBoxLayout This main pattern applies to all qooxdoo classes that support adding its children through the “.add()” mechanism. For more complex widgets, such as the ListView, Menu, SplitPane etc, QxTransformer models the parent-child relationships even when the qooxdoo API works differently. The QxTransformer showcase (to be released) contains individual pages that show the xml syntax of these widgets. For example, the qooxdoo implementation of a grid layout requires code that quickly gets confusing and is hard to maintain. QxTransformer provides you with a syntax which frees you from having to keep track of which cell you are currently adding content to. You can code a grid similar to the way you are used to with HTML tables: <qx:gridLayout Note that, for convenience, we use “qx:..” instead of “qxt:..” for rows, columns or cells even though they are pure constructs and do not correspond to “real” qooxdoo widgets. EventsYou can attach event listeners to each widget that is a subclass of qx.core.Target. The easiest way to do this with QxTransformer is to add an “onFoo” attribute. For example, <qx:atom
will log an info message once the “appear” event has been fired on the Atom and will alert when the atom has been clicked on. More sophicated event handlers are attached like so: <qx:window The “delegate” attribute delegates, as its name suggests, the event handling to an external event handler defined by <qxt:eventHandler>. This allows you to put longer pieces of code out of the GUI description. Internally, such event Handlers are implemented as methods to the main application object, so make sure you do not use names that conflict with methods of qx.application.Gui. Instead of executing event handler, you can also delegate the event (and event data) to a message subscriber by dispatching a message with the "dispatchMessage" attribute (see below). It is good practice to enclose event handling code (and all other javascript code in an qxml document) in <![CDATA[ ... ]]>, and it is required if you use characters that have a special meaning in XML such as “<” and “&”. Inside the event handler code, the event itself is available as the local variable “event”. You can also use a different variable name by setting the “args” attribute. The object itself is available as “this”: <qx:textField onChangeValue=" alert('User wrote:' + event.getData())" /> Dispatching and subscribing to messagesOne philosophy behind QxTransformer is to minimize the use of named objects and global variables as much as possible. Widget objects should work as self-contained units which do not need to know about the name of other objects - in fact, they should not even need to know about each others’ existence. Instead of calling each others’ methods, objects should issue messages in which they state what action should be performed by others and react to messages issued by other objects. This does not only prevent situations in which the non-initialization of one object breaks the application (because the named object then doesn’t exist and the method call results in a javascript error). It also allows the routing of messages beyond the scope of the client environment, for example, to the server. A simple example: An atom listens for value change in a text field: <qx:atom label="Display change-label message value">
What this code does is to create an event listener at the text field
which redirects the event data into a “change-label” message. The atom
listens for the message and changes its label with the value of the
user input. Neither object needs to know that the other one exists. On the other hand, there can be multiple senders and multiple subscribers. For example, in a different part of the application, the “change-label” message could be issued manually: qx.event.message.Bus.dispatch("change-label","Hello World!"); You don’t need to know about the specific atom or remember its global variable name, and there can be any number of other widgets which listen for the message as well. Additionally, message subscribers can listen for multiple messages at once by using truncation: <qx:atom label="Display non-object 'change-*' messages"> Scripts
Not everything can be done with the QXML, many things, in particular the main business logic of your application, has to be coded in javascript. You can put a big part of the code into event listeners and event handlers. Additionally, plain text javascript can be inserted into the page between <qxt:script> and </qxt:script> tags. It will be executed at its position in the document. As mentioned above, it is advised to enclose the code in <![CDATA[ ... ]]>. It is also possible to include external scripts this way: <qxt:script src="js/myExternalFile.js" /> Creating the main application
Please note that in the current releas (0.3alpha1), the tags are still <qx:application> and <qx:widget>, but this will change to <qxt:..> in the next release (since the tags refer to template constructs, not "real" qooxdoo widgets). QxTransformer supports two different top-level tags in the .qxml files, one for the main application and one for individual widgets. qx:applicationqxt:application creates a qooxdoo javascript document that can be run as the initial qooxdoo application. For example, if “custom.Application.qxml” contains the following code: <?xml version="1.0" encoding="utf-8"?> After running "make transform", this will create the class “custom.Application” in the file frontend/source/class/custom/Application.js. All widgets defined in this file below the root node will be added as child widgets of the main document instance in the constructor method of the generated JavaScript file. Application modularizationOnce your application grows beyond a certain size, it is not advisable to keep all the child widgets in the main .qxml file. The application code is much easier to maintain if you split your application is smaller parts. This is especially true if you are collaborating with others in the development. You can use the <qxt:include-base> and <qxt:include> tags to include qxml files in other places A typical .qxml application file might look like this: <?xml version="1.0" encoding="utf-8"?> <qx:root xmlns:qx="http://www.qxtransformer.org/qooxdoo" qooxdooVersion="0.7"><!-- application --> <qx:application title="My very special application" namespace="myApp" authors="Me and You and a Dog Named Boo" translate="true"> <!-- include bases --> <qxt:include-base name="components" path="/../../../../../qooxdoo-contrib/qcl/trunk/source/xml/components/"/> <qxt:include-base name="main" path="/includes/main/"/> <!-- this will be executed inside the constructor --> <qxt:script> this.__myArray = []; // add mixins etc. </qxt:script> <!-- start scripts --> <qxt:include base="main" src="scripts-start.xml"/> <qxt:include base="main" src="resources.xml"/> <!-- security and configuration --> <qxt:include base="main" src="security.xml"/> <qxt:include base="main" src="configuration.xml"/> <!-- components --> <qxt:include base="components" src="loading-popup-widget/loading-popup-simple.xml"/> <qxt:include base="components" src="error-alert-widget/error-alert-simple.xml"/> <qxt:include base="components" src="login-widget/login-window-simple.xml"/> <qxt:include base="components" src="splash-widget/splash-widget-simple.xml"/> <qxt:include base="components" src="authorization-widget/authorization-widget-main.xml"/> <qxt:include base="components" src="config-editor-widget/config-editor-main.xml"/> <qxt:include base="components" src="contextmenu/contextmenu.xml"/> <!-- contextmenus --> <qxt:include src="/includes/contextmenus.xml"/> <!-- popup windows --> <qxt:include src="/includes/preferences/preferences-main.xml"/> <qxt:include src="/includes/windows/folder-properties.xml"/> <!-- main window layout --> <qxt:include base="main" src="main.xml"/> <!-- admin scripts --> <qxt:include base="main" src="admin.xml"/> <!-- plugin stuff --> <qxt:include base="main" src="plugins.xml"/> <!-- final scripts / business logic --> <qxt:include base="main" src="scripts-end.xml"/> <!-- set datasource --> <qx:messageSubscriber filter="myApp.commands.datasource.change"> <![CDATA[ var datasource = message.getData(); this.getApplication().setDatasource(datasource); ]]> </qx:messageSubscriber> <!-- set datasource method --> <qxt:method name="setDatasource" arguments="datasource"> <![CDATA[
this.setUserData("datasource",datasource);
]]> </qxt:method> <!-- destructor --> <qxt:destructor> this.__myArray = null; </qxt:destructor> </qx:root> As you can see, it is possible to define methods and the constructor to the application class, although you can achieve the same result (probably in a cleaner way) by adding mixins. qx:widgetThe <qxt:widget > template will see a major overhaul, so do not rely on the behaviour described here.If you want to further modularize your code and create separate widgets of which there could be various instances, create a separate file: <?xml version="1.0" encoding="utf-8"?>
This will create the class showcase.LoginWindow in the file /frontend/source/class/custom/LoginWindow.js. After running “make transform” and “make build|source” you can instantiate the widget anywhere in your code: var w = new custom.LoginWindow;
since the qooxdoo build process automatically includes the javascript source into the final code. At the moment, you need to access the actual widget (here a window) by "w.getWidget()" TranslationWith QxTransformer, you can easily take advantage of the qooxdoo’s excellent support for translation. All you need to is to supply the attribute ‘translate=”true”’ either in <qx:application> or <qx:widget> (to translate all strings in the application or top-level widget) or in the current widget node (if you want to use it only selectively). All occurrences of widget labels will then be rendered with “this.tr(...)” in the javascript code. Please consult the qooxdoo manual on details how to make use of the qooxdoo translation system. The subpages of this page contain further details on QxTransformer. However, they are not updated to the current release and some information therein might be incorrect and we offer no support for features described there yet. In any case, if you are interested in the full power of QxTransformer and think about contributing to the project, we encourage you to skim these pages.
|