Você está na página 1de 9

Application Tutorial Application development with Lucid is very easy, and you can make full featured applications

very quickly using a combination of Dojo, and its built-in APIs. This tutorial will assume that you know how to use Dojos widget system. If you dont, it is suggested you take a look at Dojos 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 selfmanagement 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 theinit 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 somedojo.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 subclassedBorderContainer, 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 killmethod 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:

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! Application Structure Applications are Dojo modules Lucid applications are essentially dojo modules. They live in the lucid.apps namespace. The name of the module in this namespace is known as the applications system name, or sysname. All applications extend the desktop.apps._App class, which contains some bootstrap code and self-management functions. Metadata Each app has metadata associated with it, which is stored in the database. This tells Lucid what category it is, which file types it can open, and other things. Application Skeleton dojo.provide("lucid.apps.TestApp"); dojo.declare("lucid.apps.TestApp", lucid.apps._App, { init: function(args){ /*Startup code goes here*/ }, kill: function(){ /*Cleanup code goes here*/ } }); Here, dojo.provide tells Dojo's package manager that the module loaded OK, and that it does in fact have our TestApp class in it. The dojo.declare call is what makes the class for our application. As you can see, it's extending _App, which takes care of process management, and other low-level tasks in our app. An application, at the very least, has an init and a kill function. These are run by Lucid when the app starts and when it exits, respectively. You can call the kill function from any function in your code to kill the application. init, on the other hand, is only run once, and by Lucid. Your application shouldn't call the initfunction. File layout The layout for applications consists of the following: SysName/ SysName.js SysName being the sysname of your application. Depending on how complex your application is, you can take two routes when choosing a layout. Single-file applications These are good for quick applications that don't do too much. If you're writing an application that doesn't need custom widgets <dev-howto-custom-widgets> or templates, and doesn't need too much organization, you can write the entire app in one single class. Multiple-file applications Applications that need more structure can use multiple files. This makes the application much easier to maintain. Since Lucid applicatons are basically Dojo modules, we can split it up into several different files, each can have a widget, class, or other resource that the application can use. With this pattern, usually you put all the dojo.require statements in your app's main file. For example: dojo.declare("lucid.apps.MyApp"); dojo.require("lucid.apps.MyApp._base"); dojo.require("lucid.apps.MyApp.SomeClass");

dojo.require("lucid.apps.MyApp.SomeWidget"); dojo.require("lucid.apps.MyApp.submethods"); Each module coresponds to a javascript file in the MyApp folder. Fore example, MyApp._base would corespond to MyApp/_base.js. It's important that you load``_base`` before the other modules in your app, otherwise_base will overwrite the declarations. _base contains the application declaration itself: dojo.provide("lucid.apps.MyApp._base"); dojo.declare("lucid.apps.MyApp", lucid.apps._App, { // ... }); SomeClass and SomeWidget will contain some resource that we can use in our application. For example: dojo.provide("lucid.apps.MyApp.SomeClass"); dojo.declare("lucid.apps.MyApp.SomeClass", [], { // ... }); We can also add methods to our original application declaration using dojo.extend: dojo.provide("lucid.apps.MyApp.submethods"); dojo.extend(lucid.apps.MyApp, { // ... }); This way, we can separate our functions based on what they do. For example, we can have one file that just deals with UI, while another deals with some sort of calculations, etc. Accessing files/backends bundled with an application You can get the path to scripts, images, audio, etc. using dojo.moduleUrl: var soundPath = dojo.moduleUrl("lucid.apps.SystemName", "resources/sound.mp3"); Note that some APIs (such as the Sound API) require that you pass a string. Since moduleUrl returns an object containing a toString method, you need to pass the uri property of the object returned. soundPath = soundPath.uri; When used in conjunction with the lucid.xhr method, you can make calls to custom backends packaged with your app this way. lucid.xhr({ url: dojo.moduleUrl("lucid.apps.SystemName", "backends/someFile.php"), load: function(data){ console.log("Returned from backend: "+data); } }) Packaging Lucid has a package system that you can use to distribute modifications, applications, themes, and other things. Anatomy of a package Packages for Lucid are zip files that contain a meta.json file. They end in .lucid.zip. The meta.json file contains information about the package. It contains JSON. encoded data. Heres an example: { "key": "value", "key2": ["value1", "value2", "value3"], "key3": [], "key4": "value4" }

Common parameters All package share these parameters. type The type of package. Must be application, update, or theme. author The author of the contents of this package. email The author's email address. version The version of this package. maturity The package's maturity. Can be alpha, beta, rc or stable. compatible The versions that this package is compatible with. This parameter is an array. For example, if I wanted to say that my package was only compatible with version 1.0 of Lucid, I would pass ["1.0.0"]. However, if I wanted to specify that it would be compatible with both 1.0 and 1.1, I would pass["1.0.0", "1.1.0"]. Application Packages Application packages have the following directory structure: <sysname>/ (any additional files) <sysname>.js <sysname>.js.uncompressed.js (optional) meta.json They have the following key-value pairs for the meta.json file: sysname This is the system name of your application. This cannot contain any spaces, must start with a letter, and should be camel-cased. This must be the same as the names of the files in the package (see above). name This is the displayed name of your application. category The category of the application. Must be either Accessories, Development, Games, Graphics, Internet,Multimedia, Office, System, Administration, or Preferences. Casing does matter. icon (optional) The icon of the application. Either an icon class, such as icon-16-apps-accessories-calculator, or a icon file, such as calculator.png. Should be 16x16 pixels large. The icon should be placed in the application's directory (not the root of the archive). filetypes The filetypes that the application can handle. This parameter is an array, where each part of the array is a mimetype. For example, if I had an HTML editor, I would specify ["text/html"]. permissions An array with the permissions required for this app. This does not prevent the app from being launched, but just prevents it from showing up on the menu in the case that the user is lacking the permissions. Example: ["core.administration"]. Theme Packages Theme packages have the following directory structure: <name>/ (Theme files go in here) meta.json The meta.json file takes the following parameters: name The name of the theme. Must be the same as the folder in the package (see above). preview A file in the theme's directory that is a thumbnail of the theme's screenshot. This is not a full-sized screenshot.

wallpaper (optional) A file in the theme's directory that can be used as wallpaper. Update Packages Updates have the following directory structure: root/ <contents of update> update.php update.php is run after the package is installed. This script needs to detect the current version of Lucid and update it accordingly. It can detect the version from the version.json file in/desktop/dojotoolkit/desktop/resources/. Once the script has been run, the updater copies the files withinroot to the root folder of the installation.

Você também pode gostar