Fifteen minutes from now, you will have created a fully functional tic-tac-toe application that pits a human against a remote server. Since you'll be building this application with XWT, it will be instantly accessible from any computer on the internet without installing any custom software.
This tutorial assumes you are vaguely familiar with XML (or even just HTML), and that you have a text editor and a zip archiver (such as WinZip). Knowing a bit of JavaScript will also help, but it's not strictly necessary.
An XWT application consists of two pieces: the server portion and client portion.
The server side can be written in any language and run on any platform, so long as it can communicate using XML-RPC (SOAP is also supported for legacy applications).
The client side consists of an XWT Archive (an "xwar") and the XWT Core, which is bootstrapped by an ActiveX control or Java Applet known as the shoehorn.
The author of an application creates several files describing the layout (in XML), behavior (in JavaScript), and appearance (as PNG images) of the client-side UI. The author then puts those files in a zip archive and renames that archive to have an ".xwar" extension rather than a ".zip" extension. Finally, the author puts that xwar on any web server, and places a special link on a web page, pointing (indirectly) to that xwar. When end users click on the link, their browser will load an HTML page with the shoehorn embedded in it. The shoehorn in turn loads the XWT Core, which downloads the xwar, renders the user interface, and delivers mouse clicks and keypresses to the xwar's JavaScript code. This JavaScript code can then communicate with the server side of the application by making XML-RPC (or SOAP) calls.
Open up a plain text editor and type in the following code:
<xwt> </xwt>
Save this file as main.xwt.
This is the smallest legal XWT file. It does nothing. That's not too interesting, so let's add a comment at the top and two other lines to it (as we build this XWT file, changes will be shown in red):
<!-- main.xwt --> <xwt> <template thisbox="frame" width="220" height="260" color="black"> </template> </xwt>
This tells XWT to create a top-level window with a titlebar and borders (a "frame" in XWT), and that it should initially be 220 pixels wide and 260 pixels tall, with a black background.
Next, we'll want to create three rows and three columns for the cells of the tic tac toe grid. Let's start with the rows. Each row will be a <box>, which lays out its contents horizontally by default. Since the three rows themselves should be arranged vertically, we'll put them inside a <box>, and we'll set the orientation of the outer box to "vertical" to indicate that its contents (the three rows) should be arranged vertically.
<!-- main.xwt --> <xwt> <template thisbox="frame" width="220" height="260" color="black"> <box orient="vertical"> <box></box> <box></box> <box></box> </box> </template> </xwt>
Each cell of the tic-tac-toe grid is going to consist of two boxes, one inside the other. The inner boxes will each be 44 pixels by 44 pixels, and we'll want a minimum of 10 pixels between boxes to create borders between the cells.
<!-- main.xwt --> <xwt> <template thisbox="frame" width="220" height="260" color="black"> <box orient="vertical"> <box> <box hpad="5" vpad="5"> <box color="white" width="44" height="44"></box> </box> <box hpad="5" vpad="5"> <box color="white" width="44" height="44"></box> </box> <box hpad="5" vpad="5"> <box color="white" width="44" height="44"></box> </box> </box> <box> <box hpad="5" vpad="5"> <box color="white" width="44" height="44"></box> </box> <box hpad="5" vpad="5"> <box color="white" width="44" height="44"></box> </box> <box hpad="5" vpad="5"> <box color="white" width="44" height="44"></box> </box> </box> <box> <box hpad="5" vpad="5"> <box color="white" width="44" height="44"></box> </box> <box hpad="5" vpad="5"> <box color="white" width="44" height="44"></box> </box> <box hpad="5" vpad="5"> <box color="white" width="44" height="44"></box> </box> </box> </box> </template> </xwt>
Wow, that's really verbose. We had to type the same thing nine times. And when we add JavaScript code to specify the behavior of those nine grid cells, it's only going to get worse. Let's create a new template called cell.xwt (save it in the same directory as main.xwt), and put this in it:
<!-- cell.xwt --> <xwt> <template hpad="5" vpad="5"> <box color="white" width="44" height="44"> </box> </template> </xwt>
Now, we can shorten main.xwt to just this:
<!-- main.xwt --> <xwt> <template thisbox="frame" width="220" height="260" color="black"> <box orient="vertical"> <box> <cell/> <cell/> <cell/> </box> <box> <cell/> <cell/> <cell/> </box> <box> <cell/> <cell/> <cell/> </box> </box> </template> </xwt>
You'll notice that the name of the tag <cell> matches the name of the file cell.xwt. When XWT sees a tag inside the body of the <template> tag with a name other than "box", it looks for the file with that name (plus the .xwt suffix).
We're going to need a way to refer to these cells, so let's add id tags to them. The points of a compass make for a nice naming scheme, but you could use any set of names you like:
<!-- main.xwt --> <xwt> <template thisbox="frame" width="220" height="260" color="black"> <box orient="vertical"> <box> <cell id="northwest"/> <cell id="north"/> <cell id="northeast"/> </box> <box> <cell id="west"/> <cell id="center"/> <cell id="east"/> </box> <box> <cell id="southwest"/> <cell id="south"/> <cell id="southeast"/> </box> </box> </template> </xwt>
We'll also add an id tag to the inner box of cell.xwt, since it will come in handy later.
<!-- cell.xwt --> <xwt> <template hpad="5" vpad="5"> <box color="white" width="44" height="44" id="inner"> </box> </template> </xwt>
Now we need to add some behavior code so that the cells change their appearance when the user clicks on them. To keep things simple, the human will always be "x", and will always get to go first.
<!-- cell.xwt --> <xwt> <template hpad="5" vpad="5"> _Press1 = function() { if (state == "empty") { state = "x"; } } _state = function(s) { if (s == "empty") { $inner.image = null; } else if (s == "x") { $inner.image = "x"; } else if (s == "o") { $inner.image = "o"; } } <box color="white" width="44" height="44" id="inner"> </box> </template> </xwt>
This requires a bit of explanation.
The code above creates two functions, and assigns them to two properties on the cell (_Press1 and _state). Properties beginning with an underscore are special -- they are traps. When you place a function on a trap property, it gets called when other code writes a value to the corresponding property. So, for example, if you did something like this:
state = "x";
XWT will invoke the second function above (the one assigned to _state), and pass it the value "x".
The Press1 property is special -- when the user presses mouse button 1 (the leftmost button) while the mouse is on top of a box, XWT writes the value true to the Press1 property on that box. If you've assigned a function to the trap _Press1, that function will get called. In the example above, when the user clicks on a cell whose state is "empty" its state will be changed to "x".
Finally, you can use properties starting with a dollar sign to access id-tagged children of a box; the property $inner refers to the box with attribute id="inner". So when we change $inner.image, it changes the image property of the box whose id tag is "inner".
Speaking of images, we have to give XWT the PNG images for "x" and "o". When you write a string to the image property on a box, XWT looks for a file with that name and the extension ".png". So in the case above, we need to provide x.png and o.png. Right click on each of the two links below and save them in the same directory where main.xwt and cell.xwt are. Make sure the filenames are all lower-case -- XWT is case sensitive.
We're going to make one more change to cell.xwt -- we want the state to start out as "empty", so we'll add an attribute:
<!-- cell.xwt --> <xwt> <template hpad="5" vpad="5" state="empty"> _Press1 = function() { if (state == "empty") { state = "x"; } } _state = function(s) { if (s == "empty") { $inner.image = null; } else if (s == "x") { $inner.image = "x"; } else if (s == "o") { $inner.image = "o"; } } <box color="white" width="44" height="44" id="inner"> </box> </template> </xwt>
In XWT, XML attributes and JavaScript properties are the same thing. We could have made the following change instead, which would have had exactly the same effect:
<!-- cell.xwt --> <xwt> <template hpad="5" vpad="5"> _Press1 = function() { if (state == "empty") { state = "x"; } } _state = function(s) { if (s == "empty") { $inner.image = null; } else if (s == "x") { $inner.image = "x"; } else if (s == "o") { $inner.image = "o"; } } state = "empty"; <box color="white" width="44" height="44" id="inner"> </box> </template> </xwt>
Okay, now that we've got cells working, let's return to the main body. We're going to want to send the user's moves back to the server somehow. We'll do that when the user releases the first mouse button.
<!-- main.xwt --> <xwt> <template thisbox="frame" width="220" height="260" color="black"> _Release1 = function() { xwt.thread = function() { var result = xwt.xmlrpc("http://xmlrpc.xwt.org/RPC2/").TicTacToe.move( [ [ $northwest.state, $north.state, $northeast.state ], [ $west.state, $center.state, $east.state ], [ $southwest.state, $south.state, $southeast.state ] ] ); } } <box orient="vertical"> <box> <cell id="northwest"/> <cell id="north"/> <cell id="northeast"/> </box> <box> <cell id="west"/> <cell id="center"/> <cell id="east"/> </box> <box> <cell id="southwest"/> <cell id="south"/> <cell id="southeast"/> </box> </box> </template> </xwt>
There are more concise ways to do this (using loops and recursion to iterate over the boxes), but for the sake of this tutorial, we'll stick with the brute-force approach and type out each of the nine cell names.
The code we just added will be triggered when the user releases the first mouse button anywhere within the main window. When it is triggered, it will spawn a background thread. The background thread will make an XML-RPC call to the server xmlrpc.xwt.org, access the TicTacToe object, and invoke the move() method on it, passing it a 3x3 array of strings.
Why do we spawn a background thread? Well, the XML-RPC call might take a long time -- perhaps a few seconds to complete running. During this time, XWT still needs to deliver other events (such as mouse clicks, repaints, and window resizes). If we made the call without spawning a background thread, XWT wouldn't be able to deliver any events until the call returned -- and the application would appear to have locked up since it wouldn't be responding or repainting itself (on the Mac this would manifest itself as the "spinning rainbow wheel of death"). For this very reason, XWT requires that XML-RPC (and SOAP) calls can only be made from background threads. Fortunately it's very easy to create background threads -- just assign a function to xwt.thread and that function will be called in the newly-created background thread.
The last thing we need to do is actually process the response we get from the server:
<!-- main.xwt --> <xwt> <template thisbox="frame" width="220" height="260" color="black"> _Release1 = function() { $message.text = "thinking..."; xwt.thread = function() { var result = xwt.xmlrpc("http://xmlrpc.xwt.org/RPC2/").TicTacToe.move( [ [ $northwest.state, $north.state, $northeast.state ], [ $west.state, $center.state, $east.state ], [ $southwest.state, $south.state, $southeast.state ] ] ); $message.text = result.message_text; $northwest.state = result.state[0][0]; $north.state = result.state[0][1]; $northeast.state = result.state[0][2]; $west.state = result.state[1][0]; $center.state = result.state[1][1]; $east.state = result.state[1][2]; $southwest.state = result.state[2][0]; $south.state = result.state[2][1]; $southeast.state = result.state[2][2]; } } <box orient="vertical"> <box hpad="5" vpad="5" id="message" color="white" text="your move"/> <box> <cell id="northwest"/> <cell id="north"/> <cell id="northeast"/> </box> <box> <cell id="west"/> <cell id="center"/> <cell id="east"/> </box> <box> <cell id="southwest"/> <cell id="south"/> <cell id="southeast"/> </box> </box> </template> </xwt>
The move() method on our server returns an object with two properties: message_text (a string), and state (a 3x3 array of strings). We display the message_text in the display box at the top of the window, and set the state of the grid cells to what the server says they should now be.
Now, zip up all four files (cell.xwt, main.xwt, x.png, and o.png) in a zip file, and rename that file to "tictactoe.xwar". Make sure that all the file names are lower-case, and that they aren't inside any folders or directories within the zip archive. To see what the archive should look like, click here: tictactoe.xwar.
If you have access to an http server, you can put the xwar on the server, and then create a link to it in this form:
http://launch.xwt.org/stable/your-server-name-and-path
So, for example, if the xwar was at http://www.xwt.org/tictactoe.xwar, you would create a link to http://launch.xwt.org/stable/www.xwt.org/tictactoe.xwar. Notice that we left off the "http://".
When you type the link you just created into your web browser, XWT should start up and load your tic tac toe application. If you see the XWT splash screen, but you don't see the application start up, check the section on "troubleshooting" below.
To try the copy of tictactoe.xwar hosted on xwt.org, click
here:
http://launch.xwt.org/stable/xwt.org/tutorial/tictactoe/tictactoe.xwar.
There are a couple of deliberate bugs in this example:
We've left these in because fixing them would have made the example much longer, and would make the tutorial harder to follow.
If you ever have trouble with XWT, be sure to check its logs. On Win32 systems, XWT puts its logs in a file called xwt-log.txt in the Windows Temporary Directory (usually C:\WINDOWS\TEMP\, but not always!). On Mac OS X, XWT's log messages go to the Console. On all other systems, XWT's log messages go to the browser's Java Console.
XWT logs a lot of information, and adds helpful warning messages if you send it into an infinite loop or attempt to use invalid values for some things.
The server side of the tic-tac-toe application is implemented as a Java XML-RPC Servlet. It should run in any servlet container (xwt.org uses Tomcat).
The source code for the server side can be found here: TicTacToe.java and RPC2.java
If you're interested in doing more with XWT, we strongly recommend reading (or at least skimming) The XWT Reference. It's about 20 pages long, but descibes absolutely everything there is to know about XWT.
If you want to get started building applications, you can download demo.xwar, the xwar for the XWT Widget Demo. Unzip it and look in org/xwt/demo/main.xwt to see how to use all the widgets that are included. All the widgets are written in XWT (just like this tic tac toe application), and they are all built up out of nothing more than <box>es and JavaScript.
Most of these widgets are distributed under the GNU Library General Public License (not the GPL), so you can use them in closed-source applications if you like.