Application Tutorial

Application development with Lucid is very easy, and you can make full featured applications very quickly using a combination of Dojo, and it’s built-in APIs.

This tutorial will assume that you know how to use Dojo’s widget system. If you don’t, it is suggested you take a look at Dojo’s documentation. before we begin.

You can use Katana IDE to write your application, or you may use your favorite text editor. If you use a text editor, you have to create the app in the IDE first, and then open /desktop/dojotoolkit/desktop/apps/YourApp.js in the text editor.

In this tutorial, we will be writing a simple contact manager app. You will see how easy it is to make such a useful application. From the IDE, click on the ‘New’ button, and enter in ‘MyContacts’ as the System name, and ‘My Contacts’ as the displayed name. System names cannot contain spaces, should start with a letter, and should be camel-cased. Displayed names, however, can contain anything. The IDE will then create your application, and give you the skeleton below.

dojo.provide("lucid.apps.MyContacts");

dojo.declare("lucid.apps.MyContacts", desktop.apps._App, {
    init: function(args) {
        /*Startup code goes here*/
    }
    kill: function(args) {
        /*Cleanup code goes here*/
    }
});

If you're familiar with Dojo, you'll see that each app is essentially a class in the destkop.apps namespace. In other words, Lucid applications are really Dojo modules. Each app extends a base _App class, which provides some basic self-management functionality.

Let's examine this. We have two functions, init and kill. init is called when the application is launched, and kill is called when the application shuts down. Simple, right?

Creating Windows

Now let's create a window. The lucid.widget.Window widget provides us with all the windows in the desktop. We'll add the following code to our init function:

this.windows = [];
var win = new lucid.widget.Window({
    title: "My Contacts",
    onClose: dojo.hitch(this, "kill")
});
this.windows.push(win);
win.show();

Now when you launch the app, it will show an empty window. Notice the onClose event. This is very important, because the base _App class uses dojo.connect to know when the app is being closed. After a small delay, it removes itself from the process table.

In the event that your application is killed from the task manager, your app must remove all open windows. This is where that this.windows array we made before comes into play. In your kill function, add the following code:

dojo.forEach(this.windows, function(win) {
    if(!win.closed)
        win.close();
});

This will close any open windows in the this.windows array. As our app uses more windows, we can append those onto this array.

Using the Registry

Before we make our UI, we should make a Registry store to save our contacts in. Add this to the end of the init function, but before you call win.show():

var contactStore = this.contactStore = new lucid.Registry({
    name: "contacts",
    appname: this.sysname,
    data: {
        identifier: "id",
        items: []
    }
});
dojo.connect(contactStore, "onNew", function() { contactStore.save(); });
dojo.connect(contactStore, "onDelete", function() { contactStore.save(); });
dojo.connect(contactStore, "onSet", function() { contactStore.save(); });

If you're experienced with Dojo, you'll notice that lucid.Registry is a dojo.data store. This store is unique, because it can store all of it's information on the server. The name of the store is a unique name, specific to your app. Two stores should never have the same name, so make sure you're descriptive when choosing the name of the store. We provide the store with our system name (contained in this.sysname), so that one app won't override the contents of another app's store. You'll also notice that the store is saved every time it's data changes. You'll see the store in action later in the tutorial.

Writing the UI

Making a Toolbar

Now that we have our store defined, we can begin to piece together our UI. We'll start by adding some dojo.require calls to the top of our app, right underneath our dojo.declare statement.

dojo.require("dijit.Toolbar");
dojo.require("dojox.grid.DataGrid");
dojo.require("dojox.grid.cells.dijit");
dojo.require("dijit.form.TextBox");
dojo.require("dijit.form.Button");
lucid.addDojoCss("dojox/grid/resources/Grid.css");

This will load the widgets from the server when our app is loaded. Also note that we included a CSS file. This is because the dojox widgets are not included in the base stylesheet that Lucid uses, so we have to load them when we load the app.

Now that we have loaded our widgets, we can start drawing our UI. lucid.widget.Window is a subclassed BorderContainer, so you add a region property to position the element. It can be either "top", "bottom", "leading", "trailing", "left", "right", or "center". Add the following code to your init function, just before you call win.show():

var toolbar = new dijit.Toolbar({region: "top"});

var newButton = new dijit.form.Button({
    label: "New Contact",
    iconClass: "icon-16-actions-contact-new",
    onClick: dojo.hitch(this, "newContact")
});
toolbar.addChild(newButton);

var removeButton = new dijit.form.Button({
    label: "Remove Contact",
    iconClass: "icon-16-actions-edit-delete",
    onClick: dojo.hitch(this, "removeContact")
});
toolbar.addChild(removeButton);

win.addChild(toolbar);

We also need to add the methods we referenced for the toolbar to the app. Add this before the kill method in your app declaration:

newContact: function(e){

},
removeContact: function(e){

},

Adding a Grid

So, now we have a toolbar added to our window. Let's make a dojox.grid.DataGrid to display the contacts. Since Dojo's grid has editing functionality, we don't need an 'Edit Contact' dialog. Add the following code after the win.show() call:

var grid = this.grid = new dojox.grid.DataGrid({
    store: contactStore,
    region: "center",
    structure: [{
        cells: [[
            {field: "name", name: "Name", editable: true, type: dojox.grid.cells.Cell, width: "150px"},
            {field: "email", name: "Email", editable: true, type: dojox.grid.cells.Cell, width: "150px"},
            {field: "phone", name: "Phone Number", editable: true, type: dojox.grid.cells.Cell, width: "100px"},
            {field: "address", name: "Address", editable: true, type: dojox.grid.cells.Editor, editorToolbar: false, width: "auto"}
        ]]
    }]
});
win.addChild(grid);

Working with the Registry

Now that we have our grid, we can now define the methods that add and remove contacts to our store. Remember, lucid.Registry is a dojo.data store. When we made the store, we set it up so that it will save it's contents whenever a field is changed, so we don't have to worry about saving the store when we close the application.

Add the following code to your new/removeContact methods:

newContact: function(e){
    var store = this.contactStore;
    store.newItem({
        id: (new Date()).toString(), //to prevent id collisions
        name: "New Contact",
        email: "",
        phone: "",
        address: ""
    });
},
removeContact: function(e){
    this.grid.removeSelectedRows();
},

Final Product

You should now be able to add and remove contacts from the grid:

Finished contacts application window

To edit a field, double-click on the cell and input some text. To save it, click on a different cell.

Here's the full application's code:

dojo.provide("lucid.apps.MyContacts");
dojo.require("dijit.Toolbar");
dojo.require("dojox.grid.DataGrid");
dojo.require("dojox.grid.cells.dijit");
dojo.require("dijit.form.TextBox");
dojo.require("dijit.form.Button");
lucid.addDojoCss("dojox/grid/resources/Grid.css");

dojo.declare("lucid.apps.MyContacts", desktop.apps._App, {
    init: function(args) {
        this.windows = [];
        var win = new lucid.widget.Window({
            title: "My Contacts",
            onClose: dojo.hitch(this, "kill")
        });
        this.windows.push(win);

        var contactStore = this.contactStore = new lucid.Registry({
            name: "contacts",
            appname: this.sysname,
            data: {
                identifier: "id",
                items: []
            }
        });
        dojo.connect(contactStore, "onSet", function() { contactStore.save(); });

        var toolbar = new dijit.Toolbar({region: "top"});

        var newButton = new dijit.form.Button({
            label: "New Contact",
            iconClass: "icon-16-actions-contact-new",
            onClick: dojo.hitch(this, "newContact")
        });
        toolbar.addChild(newButton);

        var removeButton = new dijit.form.Button({
            label: "Remove Contact",
            iconClass: "icon-16-actions-edit-delete",
            onClick: dojo.hitch(this, "removeContact")
        });
        toolbar.addChild(removeButton);

        win.addChild(toolbar);

        win.show();

        var grid = this.grid = new dojox.grid.DataGrid({
            store: contactStore,
            region: "center",
            structure: [{
                cells: [[
                    {field: "name", name: "Name", editable: true, type: dojox.grid.cells.Cell, width: "150px"},
                    {field: "email", name: "Email", editable: true, type: dojox.grid.cells.Cell, width: "150px"},
                    {field: "phone", name: "Phone Number", editable: true, type: dojox.grid.cells.Cell, width: "100px"},
                    {field: "address", name: "Address", editable: true, type: dojox.grid.cells.Editor, editorToolbar: false, width: "auto"}
                ]]
            }]
        });
        win.addChild(grid);

        },
        newContact: function(e){
        var store = this.contactStore;
        store.newItem({
            id: (new Date()).toString(), //to prevent id collisions
            name: "New Contact",
            email: "",
            phone: "",
            address: ""
        });
    },
    removeContact: function(e){
        this.grid.removeSelectedRows();
    },
    kill: function(args) {
        dojo.forEach(this.windows, function(win) {
            if(!win.closed)
                win.close();
        });
    }
});

Summary

As you can see, we can make useful applications with minimal effort in Lucid. In just 80 lines of code, we wrote a decent contact manager. With Lucid's rich set of APIs, and Dojo's excelent widget system, application development is almost too easy!