QxWT-0.8.2-rc1
I happy to announce that a first release candidate of the GWT-Wrapper for the Qooxdoo-Widget-Library can be downloaded from our project homepage.
I started working on QxWT because I needed a GWT-Widget library providing me access to advanced controls like tables and trees and is released under a license which makes it possible to get integrated in commercial applications without paying license fees. I know there are other libraries out who already provide such a features but none of them provided me all features I wanted to have – for the record I don’t want to mess around with stores, buy in to a special domain model technology, … ).
What is QxWT
QxWT is a GWT wrapper for the qooxdoo-JS-Library. The qooxdoo-library is used for example by the RAP team as the client-side widget library. QxWT follows the qooxdoo-API as much as possible but naturally some constructs available in JavaScript can not get translated the Java 1:1 so some API methods have been adjusted. The biggest difference is that QxWT adopts the GWT-Event-Handler-System instead of wrapping the one of Qooxdoo but then once more provides 99% the same API.
One of the most important things is that Java-Developers expect to have the API documentation available in their IDE and I’ve worked with many GWT-Libraries who are not providing a good documentation. QxWT will provide full JavaDoc of all API methods when we release the final version of QxWT-0.8.2. In the RC we have documented already 99% of the widget-API but e.g. the Event-API completely lacks JavaDoc.
The second important thing is that the qooxdoo-JS-Library as well as QxWT are released under EPL which should allow you to easily use in corporate environments as well as in opensource projects.
Future plans
QxWT
- Release 0.8.2 with the following features:
- Based upon 0.8.2 release of Qooxdoo
- 95% of the Widget-API of qooxdoo
- 100% JavaDoced API
- Release 0.8.3 with the following features:
- Based upon 0.8.3 release of Qooxdoo
- Eclipse-Wizard for setting up a QxWT-Project
- Postprocess step to optimize deployment size of Qooxdoo-JS
UFaceKit for QxWT
After the release of QxWT 0.8.3 we are going to provide the following UFaceKit-Implementations
- Qx-Databinding
- Qx-Viewers
- Qx-UFaceKit-Implementation
- GWT-Integration for e4 so that you can access workbench-services from GWT-Applications (See the wonderful PDE-Editor written by Boris Bokowski for e4-0.9)
Getting started with QxWT
The following should give you a jump start on how to create a QxWT-Application from scratch (I assume you have the Google-Eclipse-Tools installed).
Setup of a new project
- Download the latest QxWT release form UFaceKit.org and unzip it somewhere on your hard drive
- Create a new GWT-Application named “MyFirstQxApp”
- Create a directory named “lib” in the “MyFirstQxApp”-Project and copy the qx-jars from the download into it
- Copy the “class”-Folder from the download into the war-Folder of your project
- Open the MyFirstQxApp.html and add the following lines directly below the title
<!-- --> <!-- Any title is fine --> <!-- --> <title>Web Application Starter Project</title> <script type="text/javascript"> window.qxgwtmodule = "myfirstqxapp"; </script>
Please note that the “myfirstqxapp” must match the value you have as a “rename-to” in your “$MODULE.gwt.xml”
Beside that you can delete the HTML-Code in the body-Tag so that it looks like this:
<body> <!-- OPTIONAL: include this if you want history support --> <iframe src="javascript:''" id="__gwt_historyFrame" tabIndex='-1' style="position:absolute;width:0;height:0;border:0"></iframe> </body>
- Add the Qx-jars to your build path
- Make your GWT-Module inherit from Qx-Libraries like this:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE module PUBLIC "-//Google Inc.//DTD Google Web Toolkit 1.6.4//EN" "http://google-web-toolkit.googlecode.com/svn/tags/1.6.4/distro-source/core/src/gwt-module.dtd"> <module rename-to='myfirstqxapp'> <!-- Inherit the core Web Toolkit stuff. --> <inherits name='com.google.gwt.user.User'/> <inherits name="org.ufacekit.qx.wrapper.QxScript" /> <inherits name="org.ufacekit.qx.QxEngine" /> </module>
- Open MyFirstQxApp.java remove everything beside the class definition
package org.ufacekit.qx.my.client; /** * Entry point classes define <code>onModuleLoad()</code>. */ public class MyFirstQxApp { }
Your first cup of QxWT
- In GWT every application starts with an entry point and so does a plain Qooxdoo-JS-Application. That’s why QxWT provides you a specialized class which bridges both systems. So the first thing you need to do is to make your GWT-EntryPoint class inherit from org.ufacekit.qx.wrapper.application.QooxdooApp
public class MyFirstQxApp extends QooxdooApp { @Override protected void run(QxAbstractGui application) { // TODO Auto-generated method stub } }
- The QxAbstractGui is the application scope into which your QxWT-Application is running in currently the only thing it provides you access to is the viewport you can add your UI-Widgets (QxComposite) to. Let’s as a first exercise create a GroupBox somewhere on the screen
protected void run(QxAbstractGui application) { QxGroupBox container = new QxGroupBox(); container.setContentPadding(16, 16, 16, 16); application.getRoot().add(container, QxOption.leftTop(50, 30, "%")); }
This code creates a groupbox and positions it 50% to the left and 30% to the top of the browser window. As you’ll notice QxWT works different to SWT because you don’t pass the parent of a widget to the constructor but you add the widget later on to the parent together with some layout informations, for swing developers this should be quite familiar.
Your ui looks like this now:
- QxWT like every other widget library provides the possibility to layout child-controls using layout managers so the next thing we do is to set a layout manager on the groupbox widget like this:
QxGridLayout layout = new QxGridLayout(9, 5); layout.setColumnAlign(0, HAlign.LEFT, VAlign.MIDDLE); layout.setColumnAlign(1, HAlign.RIGHT, VAlign.MIDDLE); container.setLayout(layout);
and add 2 children (a label and a text field) like this:
QxLabel label = new QxLabel("Greeting"); container.add(label, QxOption.rowColumn(0, 0)); QxTextField field = new QxTextField(); container.add(field, QxOption.rowColumn(0, 1));
The code should be quite self-explanatory and result in this UI.
- As a final step let’s add a button which invokes a remote services. So the first step is to add a button like this:
QxButton button = new QxButton("Submit"); container.add(button, QxOption.row(1), QxOption.column(0), QxOption.colSpan(2));
Once more quite self-explanatory beside the fact that the button is spaning 2 columns. The next thing to do is to react on button clicks which could be done in different ways but using a command which is attached to the control is the better one. So the first thing we do is to define a command and connect the button to it
QxCommand cmd = new QxCommand("CTRL+S"); button.setCommand(cmd);
As you might guess the advantage of a command is that you can attach a keyboard sequence so the command not only gets executed when the user presses the button but also when entering a the keyboard sequence CTRL+S. The last thing we need to do is to attach a listener to the command which gets informed when the command is called. So we are adding an inner-class which looks like this:
class QxExecuteHandlerImpl implements QxExecuteHandler { private QxTextField field; public QxExecuteHandlerImpl(QxTextField field) { this.field = field; } public void execute(QxExecuteEvent executeEvent) { } }
and attach it to the command like this:
cmd.addExecuteHandler(new QxExecuteHandlerImpl(field));
The application should look like this now:
- Next step is to call the remote service which should be quite familiar to people who know GWT already.
public void execute(QxExecuteEvent executeEvent) { GreetingServiceAsync service = GWT.create(GreetingService.class); service.greetServer(field.getValue(), new AsyncCallback<String>() { public void onFailure(Throwable caught) {} public void onSuccess(String result) {} }); }
and display the result for example using a QxWindow like this:
public void onFailure(Throwable caught) { QxWindow window = new QxWindow("Error"); window.setLayout(new QxVBoxLayout(10)); QxAtom atom = new QxAtom(caught.toString(),"/myfirstqxapp/qooxdoo-0.8.2-sdk/framework/source/resource/qx/icon/Oxygen/48/status/dialog-error.png"); atom.setRich(true); window.add(atom); window.center(); window.open(); } public void onSuccess(String result) { QxWindow window = new QxWindow("Remote Answer"); window.setLayout(new QxVBoxLayout(10)); QxAtom atom = new QxAtom(result,"/myfirstqxapp/qooxdoo-0.8.2-sdk/framework/source/resource/qx/icon/Oxygen/48/status/dialog-information.png"); atom.setRich(true); window.add(atom); window.center(); window.open(); }
If you now run your application, enter a text and hit the submit button or as alternative press CTRL+S your application should react like this:
The run method will by called once both framework start ups are finished and you are ready to create the application.
Here’s the complete application class once more for reference:
package org.ufacekit.qx.my.client; import org.ufacekit.qx.wrapper.application.QooxdooApp; import org.ufacekit.qx.wrapper.application.QxAbstractGui; import org.ufacekit.qx.wrapper.event.QxCommand; import org.ufacekit.qx.wrapper.event.type.gwt.QxExecuteEvent; import org.ufacekit.qx.wrapper.event.type.gwt.QxExecuteHandler; import org.ufacekit.qx.wrapper.ui.basic.QxAtom; import org.ufacekit.qx.wrapper.ui.basic.QxLabel; import org.ufacekit.qx.wrapper.ui.form.QxButton; import org.ufacekit.qx.wrapper.ui.form.QxTextField; import org.ufacekit.qx.wrapper.ui.groupbox.QxGroupBox; import org.ufacekit.qx.wrapper.ui.layout.QxGridLayout; import org.ufacekit.qx.wrapper.ui.layout.QxVBoxLayout; import org.ufacekit.qx.wrapper.ui.layout.QxGridLayout.HAlign; import org.ufacekit.qx.wrapper.ui.layout.QxGridLayout.VAlign; import org.ufacekit.qx.wrapper.ui.window.QxWindow; import org.ufacekit.qx.wrapper.util.gwt.QxOption; import com.google.gwt.core.client.GWT; import com.google.gwt.user.client.rpc.AsyncCallback; /** * Entry point classes define <code>onModuleLoad()</code>. */ public class MyFirstQxApp extends QooxdooApp { class QxExecuteHandlerImpl implements QxExecuteHandler { private QxTextField field; public QxExecuteHandlerImpl(QxTextField field) { this.field = field; } public void execute(QxExecuteEvent executeEvent) { GreetingServiceAsync service = GWT.create(GreetingService.class); service.greetServer(field.getValue(), new AsyncCallback<String>() { public void onFailure(Throwable caught) { QxWindow window = new QxWindow("Error"); window.setLayout(new QxVBoxLayout(10)); QxAtom atom = new QxAtom(caught.toString(),"/myfirstqxapp/qooxdoo-0.8.2-sdk/framework/source/resource/qx/icon/Oxygen/48/status/dialog-error.png"); atom.setRich(true); window.add(atom); window.center(); window.open(); } public void onSuccess(String result) { QxWindow window = new QxWindow("Remote Answer"); window.setLayout(new QxVBoxLayout(10)); QxAtom atom = new QxAtom(result,"/myfirstqxapp/qooxdoo-0.8.2-sdk/framework/source/resource/qx/icon/Oxygen/48/status/dialog-information.png"); atom.setRich(true); window.add(atom); window.center(); window.open(); } }); } } @Override protected void run(QxAbstractGui application) { QxGroupBox container = new QxGroupBox(); container.setContentPadding(16, 16, 16, 16); QxGridLayout layout = new QxGridLayout(9, 5); layout.setColumnAlign(0, HAlign.LEFT, VAlign.MIDDLE); layout.setColumnAlign(1, HAlign.RIGHT, VAlign.MIDDLE); container.setLayout(layout); QxLabel label = new QxLabel("Greeting"); container.add(label, QxOption.rowColumn(0, 0)); QxTextField field = new QxTextField(); container.add(field, QxOption.rowColumn(0, 1)); QxButton button = new QxButton("Submit"); container.add(button, QxOption.row(1), QxOption.column(0), QxOption.colSpan(2)); QxCommand cmd = new QxCommand("CTRL+S"); button.setCommand(cmd); cmd.addExecuteHandler(new QxExecuteHandlerImpl(field)); application.getRoot().add(container, QxOption.leftTop(50, 30, "%")); } }
If you want to learn more of the APIs you can take a look at the demo-Project in our subversion repository and naturally you can support my work on this by making a donation (See paypal button at the left of the page).
Stay tuned for more news on this and other cool stuff – there are a lot of ideas floating around in my head (some of them even translated into source-code)
Pingback: Released: QxWT-0.8.2-RC1 – GWT Wrapper for qooxdoo | Pathfinder Development | Software Developers | Blogs
Hey Tom,
your example is not working. It throws an exception “com.google.gwt.core.client.JavaScriptException: (TypeError): $wnd.qx.event.Command is not a constructor”. As this is a existing constructor in qooxdoo maybe your qx splits are not complete.
Anyway great work. If you need help with it please contact me.
Best greeting from Mainz,
Sebastian
I’ll take a look. Does this happen in hosted mode? Because I tested the example code and for me it worked.
Just tried it once more myself by downloading the rc1 files from my server and I’m not getting this exception. Can you give me more details about your GWT-Version (I’m still on 1.6.4)?
My GWT version is 1.7.1 (linux). I tried it on windows and there it works without any problem. I’ll analyse it later to get a clue what’s different between both of my systems. If I have more informations I’ll inform you.
Ok. I’m running on MacOS. I’m working towards RC2 currently and give it spin on Linux too. If you have any extra information please let me know.
I’ve tracked it down now and the Qooxdoo-JS code fails on Linux-Hosted mode (everything works if the application is compiled and run in Firefox). The offending code is DateChooser and so everything after DateChooser is not loaded – I’m now investigating it.
Pingback: Release QxWT-0.8.2-RC2 « Tomsondev Blog