Inside UJML

 

 

 

Version 1.5 - November 7, 2005

 

 

 

 

 

 

 

 

 

 

 

www.uievolution.com

Copyright 2001-2005 by UIEvolution, Inc. All rights reserved.



Audience. 4

Value of UJML. 4

Rich User Experience for Small Devices. 4

Easy to Learn. 4

Example: “Hello World!”. 4

Easy to Deploy to Many Platforms. 6

Supports Client/Server Applications. 6

Secure. 7

Supports a “No Installation” Model 7

UJML Architecture. 7

Static vs. Dynamic. 8

Partitions. 9

Application Development 11

SDK.. 12

Installation. 12

Application. 13

Display. 14

States. 15

Z-order 17

Application Logic. 19

Variables. 20

Script 21

Expressions. 24

Functions. 24

Control Flow.. 27

Events. 28

States. 29

Templates. 31

Ujinn. 35

Grid Initialization. 35

Periodic Screen Updates in a Loop. 38

Cursor 39

Removing Boxes. 42

Score Display. 51

Endgame Checking. 54

Menus. 56

Busy Cursor 69

Resuming a Game. 74

Localization. 80

Animation. 86

Reusable Components. 95

<state-machine>. 96

<include>. 99

Examples. 100

Busy. 100

Button. 100

Edit 101

Event 102

Focus. 103

FunctionKey. 103

Image. 104

Label 105

Linker 106

Menu. 107

RecordSet 108

ScrollBar 109

Stack. 109

TextBox. 110

List 110

Message Box. 111

 


 

Document History

 

Audience

This guide is for anyone interested in learning how to use Ujinn Markup Language (UJML) to easily develop and deploy applications to major phones, PDAs, computer platforms, and embedded devices.

 

Most of this guide is a tutorial which explains how to write a simple game application. Each step in the tutorial introduces one or more UJML concepts in the context of developing the game. Additionally, the guide provides general tips regarding the concepts discussed.

Value of UJML

UJML allows developers to deploy applications that support a rich user experience to a broader audience than any other technology.

Rich User Experience for Small Devices

The designers used a minimalist approach to UJML to keep the UIE Player tiny, as small as 30 KB on some Java-enabled devices, and to keep the language simple to learn and use. Yet, UJML can be used to develop applications that provide a rich user experience.

Easy to Learn

UJML is an XML-based programming markup language that supports a subset of ECMA Script; also called JavaScript.

Example: “Hello World!”

Below are examples of an application that display the text “Hello World” on the device screen. The examples are written in UJML, Java (J2ME/MIDP), and C++ (BREW). Symbian, Palm OS, and Pocket PC versions are not shown because they are similar in complexity to the BREW example. HTML and WML samples are also not shown because they are similar to UJML in terms of their simplicity.

 

This comparison illustrates the simplicity of UJML compared to other programming languages available for mobile devices. Not only does a given task require less code, but the language concepts needed to learn, understand, and remember are simpler and more concise.

 

UJML Version:

 

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE ujml PUBLIC "-//UIEVOLUTION//DTD UJML 1.1//EN" "ujml.dtd">

<ujml>

  <application>

    <display>

      <label>

        <text>Hello world</text>

      </label>

    </display>

  </application>

</ujml>

 

 

J2ME/MIDP Version:

 

import javax.microedition.midlet.*;

import javax.microedition.lcdui.*;

 

public class HelloWorld extends MIDlet implements CommandListener

{

  private Command exitCommand; // The exit command

  private Display display;   // The display for this MIDlet

 

  public HelloWorld()

  {

       display = Display.getDisplay(this);

       exitCommand = new Command("Exit", Command.SCREEN, 2);

  }

 

  public void startApp()

  {

       TextBox t = new TextBox("", "Hello World", 256, 0);

       t.addCommand(exitCommand);

       t.setCommandListener(this);

       display.setCurrent(t);

  }

 

  public void pauseApp()

  {

  }

 

  public void destroyApp(boolean unconditional)

  {

  }

 

  public void commandAction(Command c, Displayable s)

  {

       if (c == exitCommand)

         {

         destroyApp(false);

         notifyDestroyed();

       }

  }

}

 

BREW Version:

 

#include "AEEModGen.h"      // Module interface definitions

#include "AEEAppGen.h"      // Applet interface definitions

#include "AEEShell.h"       // Shell interface definitions

#include "AEEDisp.h"        // Display interface defintions

#include "helloworld.bid"   // applet class ID

#include "AEEFile.h"        // AEEFile Services

 

static boolean HelloWorld_HandleEvent(IApplet * pi, AEEEvent eCode,  uint16 wParam, uint32 dwParam);

 

int AEEClsCreateInstance(AEECLSID ClsId,IShell * pIShell,IModule * po,void ** ppObj)

{

   *ppObj = NULL;

                            

   if(ClsId == AEECLSID_HELLOWORLD)

   {

      if(AEEApplet_New(sizeof(AEEApplet), ClsId, pIShell,po,(IApplet**)ppObj,

         (AEEHANDLER)HelloWorld_HandleEvent,NULL)  == TRUE)

      {

         return (AEE_SUCCESS);

      }

   }

 

   return (EFAILED);

}

 

static boolean HelloWorld_HandleEvent(IApplet * pi, AEEEvent eCode, uint16 wParam, uint32 dwParam)

   AEEDeviceInfo di;

   AECHAR szBuf[] = {'H','e','l','l','o',' ','W','o', 'r', 'l', 'd', '\0'};

   AEEApplet * pMe = (AEEApplet*)pi;

 

   switch (eCode)

   {

      case EVT_APP_START:                       

         ISHELL_GetDeviceInfo (pMe->m_pIShell, &di);

         IDISPLAY_ClearScreen (pMe->m_pIDisplay);

         IDISPLAY_DrawText(pMe->m_pIDisplay, AEE_FONT_BOLD, szBuf, -1, 0, 0, 0, IDF_ALIGN_CENTER | IDF_ALIGN_MIDDLE);

         IDISPLAY_Update (pMe->m_pIDisplay);

         return(TRUE);

 

      case EVT_APP_STOP:

         return TRUE;

 

      default:

         break;

   }

 

   return FALSE;

}

Easy to Deploy to Many Platforms

The UIE Player is available for the following platforms:

 

·        Java

o       J2ME/MIDP (AT&T, Sprint)

o       J2ME/DoJa (DoCoMo 504i phones)

o       Personal Java

o       J2SE (Applet/Application)

·        BREW (Verizon, All-Tel, KDDI)

·        Danger Hiptop

·        Palm OS 3.5 and greater

·        Pocket PC

·        Symbian

·        Win32 ActiveX

·        Game Boy Advance (prototype)

Supports Client/Server Applications

All aspects of application and resource loading use URLs, so the programming model of UJML is designed to make it easy to create dynamic client/server networked applications as well as standalone preinstalled applications. The UIE Player supports both http and file URL schemes as well as relative URLs.

Secure

UJML applications are interpreted by the UIE Player, which protects the device from faulty or malicious applications; an application that “crashes” is simply unloaded by the UIE Player. This "sandbox" protects UJML applications from each other, and also limits the damage a malicious application can inflict on a device.

 

By default, UJML applications are not able to access a device’s features like the file system, phone book, and phone dialer. For custom UIE Players that allow access to device features and resources, the UIE Player configuration allows for flexible security policies that can limit access to resources to trusted UJML applications or partitions. The UIE Player uses a simple URL-based security policy similar to cookie security in HTML/HTTP browsers.

Supports a “No Installation” Model

The UIE Player can use a device’s local storage as a cache of applications so that the user never has to manage application installation on his or her device. The UIE Player simply fetches applications that are not in the cache from the network. When the player needs more space in the cache it discards the least recently used application.

 

This model reduces device operational cost since applications are maintained and deployed using a server, rather than requiring explicit application management on the device. It also allows application updates to be deployed transparently to users because the update is deployed to the server and the devices simply load the updated version next time the user runs the application.

UJML Architecture

UJML is an XML-based markup language that adheres to the UJML DTD. The language describes screen contents in terms of visual states. States also contain blocks of executable code which control how the player transitions between states.

 

UJML is rendered on the device using a small application called the UIE Player, the moral equivalent of an HTML browser. The term ‘player’ is used instead of ‘browser’ because UJML applications are active and dynamic, as opposed to HTML which consists of mostly static pages that are browsed. UJML is more akin to Dynamic HTML (DHTML) which supports scripting to add active and dynamic components to static HTML.

 

Unlike most other markup language browsers, the UIE Player does not directly render UJML text files. In order to maximize performance and minimize resource usage (CPU and memory), a UJML text file must be compiled into a byte code file which the UIE Player can render. The compiler compiles UJML into an efficient executable format, thus compressing the raw UJML text file to a much smaller size, efficient for transmitting over costly and low bandwidth wireless networks.

 

Static vs. Dynamic

The compiler can generate precompiled UJML files (called UJBC files) offline, or it can be invoked on-the-fly on a server. The next two diagrams show the components involved in developing and deploying UJML applications via these two methods; the first is precompiled and preloaded on the device and the second is dynamically compiled on a server in response to a request from the device. Of course, there are many variations possible between these two extremes.

 

Diagram: Precompiled Deployment

 

Diagram: Dynamic Compilation Deployment

 

In order to support both of these deployment models, the UIE Player supports the http and file URL schemes.

 

Like HTML browsers, the UIE Player supports the notion of history so that UJML applications can support the notion of “going back” to the previous application. Applications are added to the history stack in the order they are started using the _run() function. A running application removes itself from the stack, causing the previously loaded application to be restarted using the _unload() function. The specification and configuration for the URL of the application to start when the UIE Player is started on a device is platform-dependent and is documented in the Application Deployment section later in this document.

Partitions

A UJML application may consist of multiple partitions, much like a Web site may consist of multiple HTML pages. The main, or parent, partition of an application is the one that is loaded using the _run() function. Child partitions are loaded and unloaded using the _link() and _unlink() functions. This function naming convention is borrowed from the world of binary executable applications and DLLs. Depending on their usage, UJML partitions can have the functional characteristics of both HTML pages and binary DLLs. _link()’ed partitions are not added to the history stack. Instead, they are children of their parent partition and are unloaded when the parent is unloaded.

 

Only the current application on the top of the history stack and its linked partitions consume memory resources. Once an application is not on top of the history stack, all memory resources for that application are freed and made available to the new current application.

 

Tip: While an application is being loaded to the top of the history stack, as the result of the _run() function being called, the current application consumes memory resources until the new application is finished loading. So, it is important to ensure that neither the previous application nor the new application consume so much memory that both will not fit in memory at the same time.

 

One way to avoid this situation is to use an intermediate “loader application” that uses very little memory. See the “AppSplash.ujml” example later in this document for an example.

 

The following diagram shows the UIE Player’s history stack after the following has occurred:

 

1.      The player was started with the default application “App 1”. This placed “App 1” at the bottom, location 0, of the history stack.

2.      “App 1” ran “App 2” which added “App 2” to the top of the history stack, location 1.

3.      “App 2” linked partition “Part A”.

4.      “App 1” is shown in gray because it is not physically loaded in memory at this point.

 

 

After “App 2” calls the _unload() function to unload itself, the player reloads “App 1” and the history stack and memory usage looks like the diagram below:

 

On most devices, the UIE Player is able to cache UJML files locally to avoid refetching them using the network.

 

Also, on many platforms, the UIE Player supports local persistent storage in the form of property bags. This allows applications to store small amounts of data on the device in order to preserve some aspects of their state.

Application Development

In the following sections, we’ll explore many of the features of UJML as we develop the game Ujinn[1]. The game is based on Samegame and has the following basic specification:

 

Application: ujinn

 

Goal:

 

Maximize score while removing as many of the boxes from the grid as possible.

 

Game moves:

 

The player can select a box to remove if that box has at least one adjacent box of

the same color. The adjacent box must be directly above, below, left or right of

the selected box.

 

Scoring:

 

Each time a group is cleared we add (n) * (n - 1) where 'n' is the number of

boxes in the cleared group.

 

At the end of the game the penalty for remaining boxes is the number of remaining

boxes, or the player is given a bonus of (mScore / 4) if they cleared all the boxes.

 

The following screen shots illustrate the game layout and operation.

 

 

After the player selects the current box (upper left), the yellow box and the “connected” 2 yellow boxes to the right are cleared.

 

 

SDK

Each section below uses many UJML examples, all of which lead to the development of the game Ujinn. The UJML examples are contained in separate files that accompany this document. The reader is encouraged to install the SDK and run the examples in the SDK.

Installation

Follow these steps to install the UIE SDK:

 

1.      Install Java(TM) 2 Platform Standard Edition (J2SE).

Sun Microsystems' J2SE can be found at
java.sun.com/j2se. Download and install the J2SE Software Development Kit using the instructions on Sun's Web site.

2.      Set the JAVA_HOME environment variable to the J2SE SDK installation's root directory; the default is C:\j2sdk1.4.1_01. For information on setting environment variables refer to this page.

3.      Run the UIE SDK installation program.

Download the UIE SDK from
www.uievolution.com.

 

Application

A minimal UJML application is defined as:

 

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE ujml PUBLIC "-//UIEVOLUTION//DTD UJML 1.2//EN" "ujml.dtd">

<ujml>

  <application>

   

  </application>

</ujml>

File: Application.ujml

 

To run this application in the UIE SDK Debugger, follow these steps:

 

1.      Start the debugger. Use the “Start UIE Debugger” program in the Start ŕ Programs ŕ UIEvolution SDK program group.

2.      In the Select Application dialog box:

a.       Enter the file path to the Application.ujml file. If you extracted the Inside UJML ZIP file to D:\My Documents\Inside UJML, the URL to enter is file:///D:\My Documents\Inside UJML\UJML\Application.ujml (you can also use the Browse button to locate the file).

b.      Select the Flip Phone, MIDP (136x140) skin.

c.       Click OK.

3.      Click the Run button on the toolbar.



4.      The device emulator shows:


 

 

Display

Ujinn is a grid-based game so we need to display some boxes in a grid. We also want to display some game statistics like the current score, level, etc. at the top of the screen.

 

The simplest way to display something on the screen is to put it in a <display> element.   The example below draws the text “Score” and a box:

 

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE ujml PUBLIC "-//UIEVOLUTION//DTD UJML 1.2//EN" "ujml.dtd">

<ujml>

    <application>

        <display>

            <label>

                <text>Score</text>

                <x>2</x>

                <y>2</y>

                <fg>&_COLOR_BLUE;</fg>

            </label>

            <box>

                <x>5</x>

                <y>20</y>

                <width>30</width>

                <height>50</height>

                <fg>&_COLOR_RED;</fg>

                <bg>&_COLOR_GREEN;</bg>

            </box>

        </display>

    </application>

</ujml>

File: Display.ujml

 

Running the above Display.ujml in the SDK debugger yields the following in the emulator:

 

 

This example uses the default <display> element which is intended for static backgrounds because there is no way to update the contents of the default <display>.

States

For dynamic display elements, UJML allows the programmer to define additional <display> elements that can be controlled (displayed, updated, hidden) programmatically by changing the values of state variables; called state transitions.

 

A UJML state associates one or more <display> elements to the values that a variable can contain. For example, since a boolean variable can have two values it can be in one of two states, true or false. In UJML it is possible to define <display> elements for each of these values, or states.

 

In the example below, the boolean state variable sBox has a single <display> element defined for the state value true. When sBox is set to true, the box is displayed on the screen; when it is set to false, since there is no <display> defined for the value false, the box is hidden.

 

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE ujml PUBLIC "-//UIEVOLUTION//DTD UJML 1.2//EN" "ujml.dtd">

<ujml>

    <application>

        <state-variables>

            <state-var name="sBox" type="boolean"/>

        </state-variables>

        <script>

            sBox = true;

        </script>

        <states>

            <state var="sBox">

                <transition value="true">

                    <display>

                        <box>

                            <x>5</x>

                            <y>20</y>

                            <width>30</width>

                            <height>50</height>

                            <fg>&_COLOR_RED;</fg>

                            <bg>&_COLOR_GREEN;</bg>

                        </box>

                    </display>

                </transition>

            </state>

        </states>

    </application>

</ujml>

File: DisplayState.ujml

 

The above example displays:

 

 

The example below changes the previous Display.ujml application to use separate <display> elements for the text and the box; this allows the application to control them independently by setting the corresponding state variables to either true or false to show or hide the items:

 

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE ujml PUBLIC "-//UIEVOLUTION//DTD UJML 1.2//EN" "ujml.dtd">

<ujml>

    <application>

        <state-variables>

            <state-var name="sScore" type="boolean"/>

            <state-var name="sBox" type="boolean"/>

        </state-variables>

        <script>

            sScore = true;

            sBox = true;

        </script>

        <states>

            <state var="sScore">

                <transition value="true">

                    <display>

                        <label>

                            <text>Score</text>

                            <x>2</x>

                            <y>2</y>

                            <fg>&_COLOR_BLUE;</fg>

                        </label>

                    </display>

                </transition>

            </state>

            <state var="sBox">

                <transition value="true">

                    <display>

                        <box>

                            <x>5</x>

                            <y>20</y>

                            <width>30</width>

                            <height>50</height>

                            <fg>&_COLOR_RED;</fg>

                            <bg>&_COLOR_GREEN;</bg>

                        </box>

                    </display>

                </transition>

            </state>

        </states>

    </application>

</ujml>

File: DisplayStates.ujml

 

The single most important thing to remember about UJML and states is:

 

The only way to change the display is by changing the values of state variables that have <display> elements associated with them.

 

Setting a state variable to a value that it already contains will not affect the display. The value must change to change the display[2]. This means that integer state variables are more powerful than boolean variables because a boolean state variable can realistically only have one visible <display> if having the choice of hiding the item is needed; the value true is used to show it and the value false is used to hide it.

Z-order

The <display> elements are drawn on the display with a z-order, back-to-front layering, which matches the order the <display> elements appear in the UJML source file. The default <display> is in the background, lowest in z-order, and the last <display> in the file is in the foreground, highest in z-order[3].

 

The following example adds two more boxes and a background to DisplayStates.ujml. It also shows how to create and use arrays in UJML; we’ve changed sBox from a simple boolean variable to a boolean array with three elements, one for each box we want to display. Finally, the program shows how ARGB (alpha, red, green, blue) values can be specified for the colors of visual elements.

 

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE ujml PUBLIC "-//UIEVOLUTION//DTD UJML 1.2//EN" "ujml.dtd">

<ujml>

    <application>

        <state-variables>

            <state-var name="sScore" type="boolean"/>

            <state-var name="sBox" type="boolean" size="3"/>

        </state-variables>

        <script>

            sScore = true;

            sBox[ 0 ] = true;

            sBox[ 1 ] = true;

            sBox[ 2 ] = true;

        </script>

        <display>

            <box>

                <width>136</width>

                <height>140</height>

            </box>

        </display>

        <states>

            <state var="sScore">

                <transition value="true">

                    <display>

                        <label>

                            <text>Score</text>

                            <x>2</x>

                            <y>2</y>

                            <fg>&_COLOR_BLUE;</fg>

                        </label>

                    </display>

                </transition>

            </state>

            <state var="sBox" index="0">

                <transition value="true">

                    <display>

                        <box>

                            <x>5</x>

                            <y>20</y>

                            <width>30</width>

                            <height>50</height>

                            <fg>0xffcccccc</fg>

                            <bg>0xffcccccc</bg>

                        </box>

                    </display>

                </transition>

            </state>

            <state var="sBox" index="1">

                <transition value="true">

                    <display>

                        <box>

                            <x>10</x>

                            <y>25</y>

                            <width>30</width>

                            <height>50</height>

                            <fg>0xff999999</fg>

                            <bg>0xff999999</bg>

                        </box>

                    </display>

                </transition>

            </state>

            <state var="sBox" index="2">

                <transition value="true">

                    <display>

                        <box>

                            <x>15</x>

                            <y>30</y>

                            <width>30</width>

                            <height>50</height>

                            <fg>0xff666666</fg>

                            <bg>0xff666666</bg>

                        </box>

                    </display>

                </transition>

            </state>

        </states>

    </application>

</ujml>

File: DisplayZOrder.ujml

 

The program displays the following:

 

Application Logic

So far we’ve discussed how to display things on the screen and hinted at how programming logic is expressed in UJML using the <script> element in the above examples. UJML supports variables, arithmetic expressions, logical/boolean expressions, string expressions, control flow statements, functions, and events.

Variables

We’ve already seen how to declare and use state variables. Since there is a certain amount of internal overhead for state variables to support state transitions, UJML also supports simple variables which have no states associated with them. Both state variables and simple variables can be used in expressions and executable statements in the same way as variables are used in any other programming language.

 

The example below adds variables for the score value and box colors. It shows how to use XML entity definitions for declaring constant values and how to use variables and constants in display elements.

 

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE ujml PUBLIC "-//UIEVOLUTION//DTD UJML 1.2//EN" "ujml.dtd" [

    <!ENTITY MAX_BOXES "3">

    <!ENTITY BOX_WIDTH "30">

    <!ENTITY BOX_HEIGHT "50">

]>

<ujml>

    <application>

        <state-variables>

            <state-var name="sScore" type="boolean"/>

            <state-var name="sBox" type="boolean" size="&MAX_BOXES;"/>

        </state-variables>

        <variables>

            <var name="mScore" type="int"/>

            <var name="mBox" type="int" size="&MAX_BOXES;"/>

        </variables>

        <script>

            mScore = 0;

            sScore = true;

            mBox[ 0 ] = 0xffcccccc;

            sBox[ 0 ] = true;

            mBox[ 1 ] = 0xff999999;

            sBox[ 1 ] = true;

            mBox[ 2 ] = 0xff666666;

            sBox[ 2 ] = true;

        </script>

        <display>

            <box>

                <width>136</width> <height>140</height>

            </box>

        </display>

        <states>

            <state var="sScore">

                <transition value="true">

                    <display>

                        <label>

                            <text><eval>mScore</eval></text>

                            <x>2</x> <y>2</y>

                            <fg>&_COLOR_BLUE;</fg>

                        </label>

                    </display>

                </transition>

            </state>

            <state var="sBox" index="0">

                <transition value="true">

                    <display>

                        <box>

                            <x>5</x> <y>20</y>

                            <width>&BOX_WIDTH;</width>

                            <height>&BOX_HEIGHT;</height>

                            <fg><eval>mBox[ 0 ]</eval></fg>

                            <bg><eval>mBox[ 0 ]</eval></bg>

                        </box>

                    </display>

                </transition>

            </state>

            <state var="sBox" index="1">

                <transition value="true">

                    <display>

                        <box>

                            <x>10</x> <y>25</y>

                            <width>&BOX_WIDTH;</width>

                            <height>&BOX_HEIGHT;</height>

                            <fg><eval>mBox[ 1 ]</eval></fg>

                            <bg><eval>mBox[ 1 ]</eval></bg>

                        </box>

                    </display>

                </transition>

            </state>

            <state var="sBox" index="2">

                <transition value="true">

                    <display>

                        <box>

                            <x>15</x> <y>30</y>

                            <width>&BOX_WIDTH;</width>

                            <height>&BOX_HEIGHT;</height>

                            <fg><eval>mBox[ 2 ]</eval></fg>

                            <bg><eval>mBox[ 2 ]</eval></bg>

                        </box>

                    </display>

                </transition>

            </state>

        </states>

    </application>

</ujml>

File: Variables.ujml

 

The resulting display:

 

 

Script

UJML supports two scripting languages for evaluating expressions and controlling application flow. The previous examples have used UJML’s subset of ECMAScript (JavaScript). JavaScript is contained in the <script> element. Anyone that is a familiar with programming languages like C, C++, Java, or JavaScript will most likely prefer to use JavaScript in UJML applications.

 

UJML also supports UJMLscript which has the same capabilities as JavaScript but retains the XML syntax. UJMLscript is contained in the <execute> element. Developers that are unfamiliar with the above languages may find UJMLscript easier to learn initially but may find JavaScript easier later when the programming logic of the applications becomes more complex. This is because UJMLscript is verbose and difficult to read when doing complex calculations or program control flow.

 

Except for the following example that shows equivalent JavaScript and UJMLscript, this document uses JavaScript in all examples.

 

The following example is the UJMLscript equivalent of the previous Variables.ujml example:

 

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE ujml PUBLIC "-//UIEVOLUTION//DTD UJML 1.2//EN" "ujml.dtd" [

    <!ENTITY MAX_BOXES "3">

    <!ENTITY BOX_WIDTH "30">

    <!ENTITY BOX_HEIGHT "50">

]>

<ujml>

    <application>

        <state-variables>

            <state-var name="sScore" type="boolean"/>

            <state-var name="sBox" type="boolean" size="&MAX_BOXES;"/>

        </state-variables>

        <variables>

            <var name="mScore" type="int"/>

            <var name="mBox" type="int" size="&MAX_BOXES;"/>

        </variables>

        <execute>

            <set var="mScore">0</set>

            <set-state var="sScore">true</set-state>

            <set var="mBox">

                <index>0</index>

                <val>0xffcccccc</val>

            </set>

            <set-state var="sBox">

                <index>0</index>

                <val>true</val>

            </set-state>

            <set var="mBox">

                <index>1</index>

                <val>0xff999999</val>

            </set>

            <set-state var="sBox">

                <index>1</index>

                <val>true</val>

            </set-state>

            <set var="mBox">

                <index>2</index>

                <val>0xff666666</val>

            </set>

            <set-state var="sBox">

                <index>2</index>

                <val>true</val>

            </set-state>

        </execute>

        <display>

            <box>

                <width>136</width>

                <height>140</height>

            </box>

        </display>

        <states>

            <state var="sScore">

                <transition value="true">

                    <display>

                        <label>

                            <text>

                                <ref var="mScore"/>

                            </text>

                            <x>2</x>

                            <y>2</y>

                            <fg>&_COLOR_BLUE;</fg>

                        </label>

                    </display>

                </transition>

            </state>

            <state var="sBox" index="0">

                <transition value="true">

                    <display>

                        <box>

                            <x>5</x>

                            <y>20</y>

                            <width>&BOX_WIDTH;</width>

                            <height>&BOX_HEIGHT;</height>

                            <fg>

                                <ref var="mBox">

                                    <index>0</index>

                                </ref>

                            </fg>

                            <bg>

                                <ref var="mBox">

                                    <index>0</index>

                                </ref>

                            </bg>

                        </box>

                    </display>

                </transition>

            </state>

            <state var="sBox" index="1">

                <transition value="true">

                    <display>

                        <box>

                            <x>10</x>

                            <y>25</y>

                            <width>&BOX_WIDTH;</width>

                            <height>&BOX_HEIGHT;</height>

                            <fg>

                                <ref var="mBox">

                                    <index>1</index>

                                </ref>

                            </fg>

                            <bg>

                                <ref var="mBox">

                                    <index>1</index>

                                </ref>

                            </bg>

                        </box>

                    </display>

                </transition>

            </state>

            <state var="sBox" index="2">

                <transition value="true">

                    <display>

                        <box>

                            <x>15</x>

                            <y>30</y>

                            <width>&BOX_WIDTH;</width>

                            <height>&BOX_HEIGHT;</height>

                            <fg>

                                <ref var="mBox">

                                    <index>2</index>

                                </ref>

                            </fg>

                            <bg>

                                <ref var="mBox">

                                    <index>2</index>

                                </ref>

                            </bg>

                        </box>

                    </display>

                </transition>

            </state>

        </states>

    </application>

</ujml>

File: UJMLscript.ujml

Expressions

UJML supports most common mathematical, logical, and string operators. An expression can appear both in script as well as the UJML element content for elements that allow values or variable references. In this case, the expression must either be UJMLscript or a JavaScript expression within an <eval> element. However, expressions may not appear in UJML element attribute values because, in UJML, all attribute values are static constants.

 

The following are examples of valid and invalid uses of expressions:

 

Valid

Invalid

<script>

  x = y * 3;

</script>

 

<box>

  <width><eval>w / 3</eval></width>

  <height><eval>h / 3</eval></height>

</box>

<state var=”x + y” index=”z / 8”>

 

 

 

Functions

Many utility functions are built into UJML; applications can also define their own functions.

 

The example below uses expressions and several built-in functions to dynamically lay out the screen so that it will scale to the current device’s screen size. The example also shows how to use a default state to define an identical state for all the elements of an array state variable.

 

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE ujml PUBLIC "-//UIEVOLUTION//DTD UJML 1.2//EN" "ujml.dtd" [

    <!ENTITY MAX_BOXES "3">

]>

<ujml>

    <application>

        <state-variables>

            <state-var name="sScore" type="boolean"/>

            <state-var name="sBox" type="boolean" size="&MAX_BOXES;"/>

        </state-variables>

        <variables>

            <var name="mScore" type="int"/>

            <var name="mBox" type="int" size="&MAX_BOXES;"/>

            <var name="mTextHeight" type="int"/>

            <var name="mScrWidth" type="int"/>

            <var name="mScrHeight" type="int"/>

            <var name="mBoxWidth" type="int"/>

            <var name="mBoxHeight" type="int"/>

        </variables>

        <script>

            mTextHeight = _text_height( 0, 0, 0 );

            mScrWidth = _getIntProperty( &_PROPERTY_INT_SCREEN_WIDTH; );

            mScrHeight = _getIntProperty( &_PROPERTY_INT_SCREEN_HEIGHT; );

            mBoxWidth = mScrWidth / &MAX_BOXES;;

            mBoxHeight = mScrHeight - mTextHeight;

            mScore = 0;

            sScore = true;

            mBox[ 0 ] = 0xffcccccc;

            sBox[ 0 ] = true;

            mBox[ 1 ] = 0xff999999;

            sBox[ 1 ] = true;

            mBox[ 2 ] = 0xff666666;

            sBox[ 2 ] = true;

        </script>

        <display>

            <box>

                <width> <eval>mScrWidth</eval> </width>

                <height> <eval>mScrHeight</eval> </height>

                <fg>&_COLOR_BLACK;</fg>

                <bg>&_COLOR_BLACK;</bg>

            </box>

        </display>

        <states>

            <state var="sScore">

                <transition value="true">

                    <display>

                        <label>

                            <text> <eval>mScore</eval> </text>

                            <x>1</x> <y>1</y>

                            <fg>&_COLOR_YELLOW;</fg>

                        </label>

                    </display>

                </transition>

            </state>

 

Below we define a single default state for all the elements in the array state variable sBox. The main purpose of using a default state is to reduce the amount of UJML code that needs to be written when all the states for the array are identical[4].

 

            <state var="sBox" index="*">

                <transition value="true">

                    <display>

                        <box>

                            <x> <eval>_state_index() * mBoxWidth</eval> </x>

                            <y> <eval>mTextHeight</eval> </y>

                            <width> <eval>mBoxWidth</eval> </width>

                            <height> <eval>mBoxHeight</eval> </height>

                            <fg> <eval>mBox[ _state_index() ]</eval> </fg>

                            <bg> <eval>mBox[ _state_index() ]</eval> </bg>

                        </box>

                    </display>

                </transition>

            </state>

        </states>

    </application>

</ujml>

File: Functions.ujml

 

The display on several device skins in the emulator looks like these screen shots:

 

 

 

It is generally a good idea to write applications that automatically scale to the device screen. While it may be difficult or unnecessary to develop an application that looks good on both a vertical screen format and horizontal screen format, many phones have screens in the same format but with differing sizes. So, making the application look good on the same screen format (like vertical phone screens) and within the wide screen size variances possible is a good goal so that the application can be distributed to as many devices as possible with little or no modifications.

Control Flow

Following the fundamental minimalist approach to UJML, it supports only the if and while control flow constructs; switch and for are not supported.

 

The example below uses a while loop to display all the boxes in an array. A function is used to calculate the x location of each box:

 

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE ujml PUBLIC "-//UIEVOLUTION//DTD UJML 1.2//EN" "ujml.dtd" [

    <!ENTITY MAX_BOXES "3">

]>

<ujml>

    <application>

        <state-variables>

            <state-var name="sScore" type="boolean"/>

            <state-var name="sBox" type="boolean" size="&MAX_BOXES;"/>

        </state-variables>

        <variables>

            <var name="mScore" type="int"/>

            <var name="mBox" type="int" size="&MAX_BOXES;"/>

            <var name="mTextHeight" type="int"/>

            <var name="mScrWidth" type="int"/>

            <var name="mScrHeight" type="int"/>

            <var name="mBoxWidth" type="int"/>

            <var name="mBoxHeight" type="int"/>

            <var name="i" type="int"/>

        </variables>

        <functions>

            <function name="getX" type="int">

                <parameters>

                    <var name="box" type="int"/>

                </parameters>

                <variables>

                    <var name="x" type="int"/>

                </variables>

                <script>

                    x = box * mBoxWidth;

                </script>

                <return>

                    <eval>x</eval>

                </return>

            </function>

        </functions>

        <script>

            mTextHeight = _text_height( 0, 0, 0 );

            mScrWidth = _getIntProperty( &_PROPERTY_INT_SCREEN_WIDTH; );

            mScrHeight = _getIntProperty( &_PROPERTY_INT_SCREEN_HEIGHT; );

            mBoxWidth = mScrWidth / &MAX_BOXES;;

            mBoxHeight = mScrHeight - mTextHeight;

            mScore = 0;

            sScore = true;

 

            mBox[ 0 ] = 0xffcccccc;

            mBox[ 1 ] = 0xff999999;

            mBox[ 2 ] = 0xff666666;

 

            while (i &lt; &MAX_BOXES;)

            {

                sBox[ i ] = true;

                i = i + 1;

            }

        </script>

        <display>

            <box>

                <width> <eval>mScrWidth</eval> </width>

                <height> <eval>mScrHeight</eval> </height>

                <fg>&_COLOR_BLACK;</fg>

                <bg>&_COLOR_BLACK;</bg>

            </box>

        </display>

        <states>

            <state var="sScore">

                <transition value="true">

                    <display>

                        <label>

                            <text> <eval>mScore</eval> </text>

                            <x>1</x> <y>1</y>

                            <fg>&_COLOR_YELLOW;</fg>

                        </label>

                    </display>

                </transition>

            </state>

            <state var="sBox" index="*">

                <transition value="true">

                    <display>

                        <box>

                            <x><eval>getX( _state_index() )</eval></x>

                            <y> <eval>mTextHeight</eval> </y>

                            <width> <eval>mBoxWidth</eval> </width>

                            <height> <eval>mBoxHeight</eval> </height>

                            <fg> <eval>mBox[ _state_index() ]</eval> </fg>

                            <bg> <eval>mBox[ _state_index() ]</eval> </bg>

                        </box>

                    </display>

                </transition>

            </state>

        </states>

    </application>

</ujml>

Events

User actions and system status changes are communicated to a UJML application using events.

 

The following example adds an event handler when the F1 function key is pressed by the user. The handler unloads the application when F1 is pressed.

 

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE ujml PUBLIC "-//UIEVOLUTION//DTD UJML 1.2//EN" "ujml.dtd" [

    <!ENTITY MAX_BOXES "3">

]>

<ujml>

    <application>

        <state-variables>

            …

        </state-variables>

        <variables>

            …

        </variables>

        <script>

            …

        </script>

        <display>

            <box>

                <width> <eval>mScrWidth</eval> </width>

                <height> <eval>mScrHeight</eval> </height>

                <fg>&_COLOR_BLACK;</fg>

                <bg>&_COLOR_BLACK;</bg>

            </box>

            <fn>

                <text>Back</text>

                <event name="onselect">

                    <accelerators>

                        <key>F1</key>

                    </accelerators>

                    <script>

                        _unload();

                    </script>

                </event>

            </fn>

        </display>

        <states>

            …

        </states>

    </application>

</ujml>

            File: Events.ujml

 

Tip: Always use the F1 key for Back functionality because this behavior is required by some platforms, like BREW, and it fits well with the overall user experience of many devices since most phones have the notion of Back or Clear buttons and Sony’s CLIE PDAs also have a Back button.

 

Events may only be associated with visual element in a <display> element.

 

Tip: To create event handlers when a visible visual element is not desired, use an empty or transparent <box> element.

States

In additions to defining <display> elements, states can define application logic to be executed when the state becomes active (the state variable changes to the specified value in the <transition> element).

 

The following example displays a message box and uses a delayed script to hide the box after five seconds.

 

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE ujml PUBLIC "-//UIEVOLUTION//DTD UJML 1.2//EN" "ujml.dtd" [

    <!ENTITY MAX_BOXES "3">

]>

<ujml>

    <application>

        <state-variables>

          …

            <state-var name="sMessageBox" type="boolean"/>

        </state-variables>

        <variables>

          …

        </variables>

        <script>

          …

            sMessageBox = true;

        </script>

        <display>

          …

        </display>

        <states>

          …

 

When the state variable sMessageBox is set to true, the transition below is executed by the UIE Player. First, the <display> element is processed and added to the UIE Player’s internal “display list” which it uses to paint the screen. Next, the <script> element is processed; however, since it follows a <delay> element, instead of executing the <script> element, the UIE Player adds the element to a queue so that it will be executed after a five second delay.

 

            <state var="sMessageBox">

                <transition value="true">

                    <display>

                        <multi-label>

                            <text>This is a message box.</text>

                            <x>10</x>

                            <y>30</y>

                            <width><eval>mScrWidth - 20</eval></width>

                            <height><eval>mScrHeight - 60</eval></height>

                            <fg>&_COLOR_LIME;</fg>

                            <bg>&_COLOR_MAROON;</bg>

                        </multi-label>

                    </display>

                    <delay>5000</delay>

                    <script>

                        sMessageBox = false;

                    </script>

                </transition>

            </state>

        </states>

    </application>

</ujml>

            File: ScriptStates.ujml

 

 

Common reasons for using script in a state:

 

·        Delayed logic

·        Timer-based logic

·        Animation

Templates

Because the Ujinn game uses an 8x8 grid for the game play area, we need to update our sample application to use a two-dimensional state variable array. We also introduce a color map array which is used for the game piece colors; later, we’ll initialize it based on whether or not the device supports color or grayscale.

 

The changes to our application are shown in bold below:

 

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE ujml PUBLIC "-//UIEVOLUTION//DTD UJML 1.2//EN" "ujml.dtd" [

    <!ENTITY MAX_ROWS "8">

    <!ENTITY MAX_COLS "8">

    <!ENTITY MAX_COLORS "4">

]>

<ujml>

    <application>

        <state-variables>

            <state-var name="sBoxes" type="boolean" size="&MAX_ROWS;,&MAX_COLS;"/>

            …

        </state-variables>

        <variables>

            <var name="mBoxes" type="int" size="&MAX_ROWS;,&MAX_COLS;"/>

            <var name="mColorMap" type="int" size="&MAX_COLORS;"/>

            <var name="r" type="int"/>

            <var name="c" type="int"/>

            …

        </variables>

        <script>

            …

            mColorMap[ 0 ] = 0xffcccccc;

            mColorMap[ 1 ] = 0xff999999;

            mColorMap[ 2 ] = 0xff666666;

            mColorMap[ 3 ] = 0xff333333;

 

            while (r &lt; &MAX_ROWS;)

            {

                c = 0;

 

                while (c &lt; &MAX_COLS;)

                {

                    mBoxes[ r ][ c ] = mColorMap[ (r + c) % &MAX_COLORS; ];

                    sBoxes[ r ][ c ] = true;

                    c = c + 1;

                }

 

                r = r + 1;

            }

        </script>

        <display>

            …

        </display>

        <states>

            …

            <state var="sBoxes" index="*,*">

                <transition value="true">

                    <display>

                        <box>

                            <x> <eval>_state_index(1) * mBoxWidth</eval> </x>

                            <y>

                              <eval>mTextHeight + (_state_index(0) * mBoxHeight)</eval>

                            </y>

                            <width> <eval>mBoxWidth</eval> </width>

                            <height> <eval>mBoxHeight</eval> </height>

                            <fg>

                             <eval>mBoxes[ _state_index(0) ][ _state_index(1) ]</eval>

                           </fg>

                            <bg>

                            <eval>mBoxes[ _state_index(0) ][ _state_index(1) ]</eval>

                           </bg>

                        </box>

                    </display>

                </transition>

            </state>

            …

        </states>

    </application>

</ujml>

            file: ArrayStates.ujml

 

This program displays the following:

 

The UIE SDK Debugger provides important information regarding network traffic for an application; it also can emulate network client/server latency and data transfer bandwidths. Notice that the debugger shows the application's data transfer size in the Requests tab. Also notice that our sample application has grown in size after changing it to an 8x8 grid.

 

 

The reason for the size increase is because the application now has 61 additional display states. This may be a bit surprising since the default state appears insignificant:

 

            <state var="sBoxes" index="*,*">

                <transition value="true">

                    <display>

                        <box>

                            <x> <eval>_state_index(1) * mBoxWidth</eval> </x>

                            <y>

                              <eval>mTextHeight + (_state_index(0) * mBoxHeight)</eval>

                            </y>

                            <width> <eval>mBoxWidth</eval> </width>

                            <height> <eval>mBoxHeight</eval> </height>

                            <fg>

                             <eval>mBoxes[ _state_index(0) ][ _state_index(1) ]</eval>

                           </fg>

                            <bg>

                            <eval>mBoxes[ _state_index(0) ][ _state_index(1) ]</eval>

                           </bg>

                        </box>

                    </display>

                </transition>

            </state>

 

However, a default state is just shorthand that allows the developer to use a single simple syntax to define an array of identical states, one for each state variable array element. So, this innocent-looking default state expands to 64 individual states when compiled. Hence, the increased size of the file.

 

UJML provides a way to define a display template which can be referenced from a display state and expanded on the UIE Player. This is not only useful for our particular situation where we want to minimize the size of the application, but it can also be used to define reusable sections of display elements.

 

The example below converts our application to use a template for each grid display state:

 

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE ujml PUBLIC "-//UIEVOLUTION//DTD UJML 1.2//EN" "ujml.dtd" [

    …

]>

<ujml>

    <application>

        <state-variables>

            …

        </variables>

        <templates>

            <template name="tBox">

                <display>

                    <box>

                        <x> <eval>c * mBoxWidth</eval> </x>

                        <y> <eval>mTextHeight + (r * mBoxHeight)</eval> </y>

                        <width> <eval>mBoxWidth</eval> </width>

                        <height> <eval>mBoxHeight</eval> </height>

                        <fg>

                            <eval>mBoxes[ r ][ c ]</eval>

                        </fg>

                        <bg>

                            <eval>mBoxes[ r ][ c ]</eval>

                        </bg>

                    </box>

                </display>

            </template>

        </templates>

        <script>

            …

        </script>

        <display>

            …

        </display>

        <states>

            …

            <state var="sBoxes" index="*,*">

                <transition value="true">

                    <display>

                        <expand template="tBox"/>

                    </display>

                </transition>

            </state>

            …

        </states>

    </application>

</ujml>

            file: Templates.ujml

 

Notice the size reduction as a result of using a template:

 

As before, there are still 64 display states, but now each state simply references the template which generates much less byte code  compared to the inline display elements used in the previous example.

 

Tip: Templates can only reference global variables so caution must be taken to ensure the global variable values are in sync with the state transitions when using templates. One way is to show/hide display states that use templates using a function rather than directly setting the state variable. This function can set up the global variables used by the template before setting the state variable to a value that makes the display state visible. This technique will be used later as we develop the Ujinn game.

 

Ujinn

We now have many of the basic UJML building blocks that we need to implement the Ujinn game. So, let’s build our game.

Grid Initialization

The Ujinn grid is a randomly arranged set of colors. The first step is to update our sample application to initialize the grid.

 

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE ujml PUBLIC "-//UIEVOLUTION//DTD UJML 1.2//EN" "../ujml.dtd" [

    <!ENTITY and "&amp;&amp;">

    <!ENTITY SCORE_ITEM_FG "&_COLOR_WHITE;">

    <!ENTITY SCORE_ITEM_BG "0xff000066">

    <!-- Limits -->

    <!ENTITY MAX_ROWS "8">

    <!ENTITY MAX_COLS "8">

    <!ENTITY MAX_COLORS "4">

    <!ENTITY CLEARING_COLOR "&_COLOR_WHITE;">

    <!ENTITY CLEARED_COLOR "&_COLOR_TRANSPARENT;">

]>

 

<ujml>

    <application>

        <state-variables>

            <state-var name="sBoxes" type="boolean" size="&MAX_ROWS;,&MAX_COLS;"/>

        </state-variables>

        <variables>

            <var name="mTextHeight" type="int"/>

            <var name="mScrHeight" type="int"/>

            <var name="mScrWidth" type="int"/>

            <var name="mBoxHeight" type="int"/>

            <var name="mBoxWidth" type="int"/>

            <var name="mColorMap" type="int" size="&MAX_COLORS;,2"/>

            <var name="tBoxRow" type="int"/>

            <var name="tBoxCol" type="int"/>

            <var name="gSeed" type="int"/>

            <var name="mBoxes" type="int" size="&MAX_ROWS;,&MAX_COLS;,2"/>

        </variables>

        <functions>

            <function name="setColorMapEntry" type="void">

                <parameters>

                    <var name="entry" type="int"/>

                    <var name="color1" type="int"/>

                    <var name="color2" type="int"/>

                </parameters>

                <script>

                    mColorMap[ entry ][ 0 ] = color1;

                    mColorMap[ entry ][ 1 ] = color2;

                </script>

            </function>

            <function name="setupColorMap" type="void">

                <script>

                    if (_getBooleanProperty( &_PROPERTY_BOOLEAN_COLOR_CAPABLE; ))

                    {

                        setColorMapEntry( 0, 0xffff0000, 0xffdd0000 );

                        setColorMapEntry( 1, 0xff00ff00, 0xff00dd00);

                        setColorMapEntry( 2, 0xff0000ff, 0xff0000dd);

                        setColorMapEntry( 3, 0xffffff00, 0xffdddd00);

                    }

                    else

                    {

                        setColorMapEntry( 0, 0xff000000, 0xffcccccc);

                        setColorMapEntry( 1, 0xff555555, 0xffcccccc);

                        setColorMapEntry( 2, 0xffaaaaaa, 0xffcccccc);

                        setColorMapEntry( 3, 0xffdddddd, 0xffcccccc);

                    }

                </script>

            </function>

            <function name="start" type="void">

                <variables>

                    <var name="row" type="int"/>

                    <var name="col" type="int"/>

                </variables>

                <script>

                    gSeed = _srand( 101 );

 

                    while (col &lt; &MAX_COLS;)

                    {

                        row = 0;

   

                        while (row &lt; &MAX_ROWS;)

                        {

                            mBoxes[row][col][0] = mColorMap[ gSeed % &MAX_COLORS; ][0];

                            mBoxes[row][col][1] = mColorMap[ gSeed % &MAX_COLORS; ][1];

                            tBoxRow = row;

                            tBoxCol = col;

                            sBoxes[ tBoxRow ][ tBoxCol ] = true;

                            gSeed = _srand( gSeed );

   

                            row = row + 1;

                        }

   

                        col = col + 1;

                    }

 

                </script>

            </function>

        </functions>

        <templates>

            <template name="tBox">

                <display>

                    <box>

                        <x> <eval>tBoxCol * mBoxWidth</eval> </x>

                        <y> <eval>tBoxRow * mBoxHeight + mTextHeight</eval> </y>

                        <width> <eval>mBoxWidth</eval> </width>

                        <height> <eval>mBoxHeight</eval> </height>

                        <fg> <eval>mBoxes[ tBoxRow ][ tBoxCol ][ 1 ]</eval> </fg>

                        <bg> <eval>mBoxes[ tBoxRow ][ tBoxCol ][ 0 ]</eval> </bg>

                        <box>

                            <x> <eval>mBoxWidth / 4</eval> </x>

                            <y> <eval>mBoxHeight / 4</eval> </y>

                            <width> <eval>mBoxWidth / 2</eval> </width>

                            <height> <eval>mBoxHeight / 2</eval> </height>

                            <fg> <eval>mBoxes[ tBoxRow ][ tBoxCol ][ 1 ]</eval> </fg>

                            <bg> <eval>mBoxes[ tBoxRow ][ tBoxCol ][ 1 ]</eval> </bg>

                        </box>

                    </box>

                </display>

            </template>

        </templates>

        <script>

            mTextHeight = _text_height( 0, 0, 0 );

            mScrWidth = _getIntProperty( &_PROPERTY_INT_SCREEN_WIDTH; );

            mScrHeight = _getIntProperty( &_PROPERTY_INT_SCREEN_HEIGHT; );

            mBoxWidth = mScrWidth / &MAX_COLS;;

            mBoxHeight = (mScrHeight - mTextHeight) / &MAX_ROWS;;

            setupColorMap();

            start();

        </script>

        <display>

            <box>

                <width> <eval>mScrWidth</eval> </width>

                <height> <eval>mScrHeight</eval> </height>

                <fg>&_COLOR_BLACK;</fg>

                <bg>&_COLOR_BLACK;</bg>

            </box>

            <fn>

                <text>Back</text>

                <event name="onselect">

                    <accelerators> <key>F1</key> </accelerators>

                    <script>

                        _unload();

                    </script>

                </event>

            </fn>

        </display>

        <states>

            <state var="sBoxes" index="*,*">

                <transition value="true">

                    <display>

                        <expand template="tBox"/>

                    </display>

                </transition>

            </state>

        </states>

    </application>

</ujml>

            File: ujinn1.ujml

 

 

Notice that we now use a random number generator to choose a color for each box. Also, the colors are kept in a color map which is initialized based on whether or not the device supports color. The boxes are now drawn using two boxes with slightly different shades of the same color; this helps to make the boxes more visible, especially for slightly color-blind users.

 

The start() function does the work of initializing the box colors and setting the box state to true to make each box visible. While using a function for updating the display is a workable approach, it can cause delays that exceed the user’s patience. For example, running ujinn1.ujml on some phones initializes the screen very slowly.[RA1] 

 

The reason for the delay is that the UIE Player only updates the screen when it is idle; which means no UJML code is executing. Therefore, as long as the start() function is executing, all the state transitions that update the display are not visible to the user until the function is finished.

Periodic Screen Updates in a Loop

A simple way to update the screen while the grid is being initialized is to implement the start() function using states with <delay> elements that later force the application to idle for a moment so that the UIE Player can update the screen.

 

Let’s rewrite the start() function to use states and update the screen after each column is initialized. This gives the user more immediate visual feedback to show that something useful is happening and progress is being made.

 

<ujml>

    <application>

        <state-variables>

            <state-var name="sBoxes" type="boolean" size="&MAX_ROWS;,&MAX_COLS;"/>

            <state-var name="sStart" type="boolean"/>

        </state-variables>

        <variables>

           …

            <var name="gCol" type="int"/>

        </variables>

        <functions>

           …

        </functions>

        <templates>

           …

        </templates>

        <script>

           …

            gSeed = _srand( 101 );

            gCol = 0;

            sStart = true;

        </script>

        <display>

           …

        </display>

        <states>

           …

            <state var="sStart">

                <transition value="true">

                    <delay>20</delay>

                    <variables>

                        <var name="row" type="int"/>

                    </variables>

                    <script>

                        row = 0;

   

                        while (row &lt; &MAX_ROWS;)

                        {

                            mBoxes[row][gCol][0] = mColorMap[ gSeed % &MAX_COLORS; ][0];

                            mBoxes[row][gCol][1] = mColorMap[ gSeed % &MAX_COLORS; ][1];

                            tBoxRow = row;

                            tBoxCol = gCol;

                            sBoxes[ tBoxRow ][ tBoxCol ] = true;

                            gSeed = _srand( gSeed );

   

                            row = row + 1;

                        }

   

                        gCol = gCol + 1;

 

We execute the “sStart == true” transition for each column. Once gCol is equal to MAX_COLS, sStart is set to false and we terminate the loop.

 

                        _clear_state( sStart );

                        sStart = gCol &lt; &MAX_COLS;;

                    </script>

                </transition>

            </state>

        </states>

    </application>

</ujml>

            File: ujinn2.ujml

 

In the revised Ujinn, we’ve removed the start() function and moved the initialization loop into a state, sStart. The outer loop for each column is now controlled using a global gCol variable which is used at the end of the state script to determine if we should transition to the true case again; this will happen for each column less than MAX_COLS. The key thing to notice is how a small delay is used to force the screen to be refreshed, because it forces the UIE Player to idle.

 

Tip: Use the <delay> element in long loops to update the screen periodically.

Cursor

The user needs a way to interact with the game. Phones typically have a direction pad and a Fire or Select button. PDAs tend to be more stylus-oriented. In the revision of Ujinn below, we add a cursor and stylus input to the game.

 

<ujml>

    <application>

        <state-variables>

            …

            <state-var name="sCursor" type="boolean"/>

        </state-variables>

        <variables>

            …

            <var name="mCursorCol" type="int"/>

            <var name="mCursorRow" type="int"/>

        </variables>

        <functions>

            …

            <function name="setCursor" type="void">

                <parameters>

                    <var name="row" type="int"/>

                    <var name="col" type="int"/>

                </parameters>

                <script>

                    mCursorRow = row;

                    mCursorCol = col;

                    sCursor = false;

                    sCursor = true;

                </script>

            </function>

        </functions>

        …

        <states>

            …

            <state var="sCursor">

                <transition value="true">

                    <display>

 

The first box captures stylus taps. The event handler converts the stylus location to a box location and moves the cursor to the box.

 

                        <box>

                            <y> <eval>mTextHeight</eval> </y>

                            <width> <eval>mBoxWidth * &MAX_COLS;</eval> </width>

                            <height> <eval>mBoxHeight * &MAX_ROWS;</eval> </height>

                            <fg>&_COLOR_TRANSPARENT;</fg>

                            <bg>&_COLOR_TRANSPARENT;</bg>

                            <event name="onselect">

                                <script>

                                    setCursor(

                                        _getIntProperty( &_VALUE_INT_ONSELECT_Y_REL; ) /

                                         mBoxHeight,

                                        _getIntProperty( &_VALUE_INT_ONSELECT_X_REL; ) /

                                        mBoxWidth );

                                </script>

                            </event>

                        </box>

                        <box>

                            <x>

                                <eval>mCursorCol * mBoxWidth + mBoxWidth / 2 - 2</eval>

                            </x>

                            <y>

                                <eval>(mCursorRow * mBoxHeight) + mTextHeight + 2</eval>

                            </y>

                            <width> <eval>4</eval> </width>

                            <height> <eval>mBoxHeight - 4</eval> </height>

                        </box>

                        <box>

                            <x> <eval>mCursorCol * mBoxWidth + 2</eval> </x>

                            <y>

                                <eval>(mCursorRow * mBoxHeight) + mTextHeight +

                                    mBoxHeight / 2 - 2</eval>

                            </y>

                            <width> <eval>mBoxWidth - 4</eval> </width>

                            <height> <eval>4</eval> </height>

                            <event name="onselect">

                                <accelerators> <key>LEFT</key> </accelerators>

                                <script>

                                    setCursor( mCursorRow, (mCursorCol + &MAX_COLS; - 1)

                                                           % &MAX_COLS; );

                                </script>

                            </event>

                        </box>

                        <box>

                            <x>

                                <eval>mCursorCol * mBoxWidth + mBoxWidth / 2 - 2</eval>

                            </x>

                            <y>

                                <eval>(mCursorRow * mBoxHeight) + mTextHeight +

                                      mBoxHeight / 2 - 2</eval>

                            </y>

                            <width> <eval>4</eval> </width>

                            <height> <eval>4</eval> </height>

                            <bg> <eval>mBoxes[ mCursorRow ][ mCursorCol ][1]</eval></bg>

                            <event name="onselect">

                                <accelerators> <key>RIGHT</key> </accelerators>

                                <script>

                                    setCursor( mCursorRow, (mCursorCol+1) % &MAX_COLS; );

                                </script>

                            </event>

                        </box>

                        <box>

                            <event name="onselect">

                                <accelerators> <key>UP</key> </accelerators>

                                <script>

                                    if (mCursorRow == 0)

                                    {

                                        <!-- move to bottom of previous column -->

                                        setCursor( &MAX_ROWS; - 1,

                                                   (mCursorCol + &MAX_COLS; - 1) %

                                                   &MAX_COLS; );

                                    }

                                    else

                                    {

                                        <!-- move to previous row -->

                                        setCursor( mCursorRow - 1, mCursorCol );

                                    }

                                </script>

                            </event>

                        </box>

                        <box>

                            <event name="onselect">

                                <accelerators> <key>DOWN</key> </accelerators>

                                <script>

                                    if (mCursorRow == (&MAX_ROWS; - 1))

                                    {

                                        <!-- move to top of next column -->

                                        setCursor( 0, (mCursorCol + 1) % &MAX_COLS; );

                                    }

                                    else

                                    {

                                        <!-- move to next row -->

                                        setCursor( mCursorRow + 1, mCursorCol );

                                    }

                                </script>

                            </event>

                        </box>

                        <box>

                            <x> <eval>mCursorCol * mBoxWidth</eval> </x>

                            <y> <eval>(mCursorRow * mBoxHeight) + mTextHeight</eval> </y>

                            <width> <eval>mBoxWidth</eval> </width>

                            <height> <eval>mBoxHeight</eval> </height>

                            <fg>&_COLOR_TRANSPARENT;</fg>

                            <bg>&_COLOR_TRANSPARENT;</bg>

                        </box>

                    </display>

                </transition>

            </state>

            <state var="sStart">

            …

            </state>

        </states>

    </application>

</ujml>

            File: ujinn3.ujml

 

Adding the cursor requires adding a new state variable, sCursor, to control the visibility of the cursor and the associated display elements. We also add event handlers for Up, Down, Left, and Right key presses. Finally, we add an event handler for the stylus which converts the x and y coordinates of a stylus tap to the corresponding box in the game grid.

 

The revised Ujinn game now displays a cursor in the upper left corner of the grid. On both phones and PDAs, the left, right, up, and down buttons move the cursor. On a PDA, tapping on a box will cause the cursor to move to the box.

 

 

 

 

Removing Boxes

Once the user can select a box, the next step is to allow the user to remove groups, starting with the box selected by the cursor. The application searches adjacent boxes to see if they are the same color as the box under the cursor. Once no more adjacent matches can be found, the group is deleted and the screen is redrawn.

 

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE ujml PUBLIC "-//UIEVOLUTION//DTD UJML 1.4//EN" "ujml.dtd" [

    <!ENTITY and "&amp;&amp;">

    <!-- Colors -->

    <!ENTITY SCORE_ITEM_FG "&_COLOR_WHITE;">

    <!ENTITY SCORE_ITEM_BG "0xff000066">

    <!-- Limits -->

    <!ENTITY MAX_ROWS "8">

    <!ENTITY MAX_COLS "8">

    <!ENTITY MAX_COLORS "4">

    <!ENTITY CLEARING_COLOR "&_COLOR_WHITE;">

    <!ENTITY CLEARED_COLOR "&_COLOR_TRANSPARENT;">

]>

<ujml>

    <application>

       <state-machines>

           <include file="ujlib/stack.ujml" state-machine="IntStack"/>

        /state-machines>

 

        <state-variables>

                                  …

            <state-var name="sDelayedRemoveBoxes" type="boolean"/>

            <state-var name="sDelayedSelectBox" type="boolean"/>

            <state-var name="sRemoveColumns" type="boolean"/>

        </state-variables>

        <variables>

            <var name="mTextHeight" type="int"/>

            <var name="mScrHeight" type="int"/>

            <var name="mScrWidth" type="int"/>

            <var name="mBoxHeight" type="int"/>

            <var name="mBoxWidth" type="int"/>

            <var name="mColorMap" type="int" size="&MAX_COLORS;,2"/>

            <var name="mRemoveBoxesFromColumn" type="boolean" size="&MAX_COLS;"/>

            <var name="tBoxRow" type="int"/>

            <var name="tBoxCol" type="int"/>

            <var name="mColorMapIndex" type="int"/>

            <var name="mColorMapBoxSize" type="int"/>

            <var name="mCursorCol" type="int"/>

            <var name="mCursorRow" type="int"/>

            <var name="mNumBoxes" type="int"/>

            <var name="mNumBoxesInColumn" type="int" size="&MAX_COLS;"/>

            <var name="mBoxes" type="int" size="&MAX_ROWS;,&MAX_COLS;,2"/>

            <var name="mColumnHasRemovableBoxes" type="boolean" size="&MAX_COLS;"/>

            <!-- Globals used for removing columns -->

            <var name="mNumBoxesCleared" type="int"/>

            <var name="gI" type="int"/>

            <var name="gCol" type="int"/>

            <var name="gRow" type="int"/>

            <var name="gSeed" type="int"/>

            <var name="gAvailCol" type="int"/>

            <var name="gRemoveColumnsDelay" type="int"/>

        </variables>

        <functions>

                                  …

            <function name="checkBoxColor" type="void">

                <parameters>

                    <var name="row" type="int"/>

                    <var name="col" type="int"/>

                    <var name="color" type="int"/>

                </parameters>

                <script>

                    if (color == mBoxes[ row ][ col ][ 0 ])

                    {

                        IntStack.push( row );

                        IntStack.push( col );

                    }

                </script>

            </function>

                                  …

            <function name="selectBox" type="void">

                <parameters>

                    <var name="row" type="int"/>

                    <var name="col" type="int"/>

                </parameters>

                <variables>

                    <var name="selectedColor" type="int"/>

                    <var name="selectedInnerColor" type="int"/>

                    <var name="numBoxesToBeRemoved" type="int"/>

                    <var name="i" type="int"/>

                </variables>

                <script>

                    <!-- don't allow the user to select a cleared or clearing box -->

                    if ((mBoxes[row][col][0] != &CLEARING_COLOR;) &and;

                        (mBoxes[row][col][0] != &CLEARED_COLOR;))

                    {

                        <!-- remember selected color -->

                        selectedColor = mBoxes[ row ][ col ][ 0 ];

                        selectedInnerColor = mBoxes[ row ][ col ][ 1 ];

 

                        <!-- put selected box on stack -->

                        IntStack.push( row );

                        IntStack.push( col );

 

                        <!-- find boxes of the selectec color to remove -->

                        while (IntStack.size() &gt; 0)

                        {

                            <!-- process current box on the stack -->

                            col = IntStack.pop();

                            row = IntStack.pop();

 

                            <!-- mark the box to be cleared -->

                            mBoxes[ row ][ col ][ 0 ] = &CLEARING_COLOR;;

                            mBoxes[ row ][ col ][ 1 ] = &CLEARING_COLOR;;

                            repaintBox( row, col );

 

                            <!-- increment count of boxes to be cleared -->

                            numBoxesToBeRemoved = numBoxesToBeRemoved + 1;

 

                            <!-- mark column as having a box to be removed -->

                            mRemoveBoxesFromColumn[ col ] = true;

 

                            <!-- Now check neighboring boxes to see if they are the same

                            color -->

                            <!-- Check box above -->

                            if (row &gt; 0)

                            {

                                checkBoxColor( row - 1, col, selectedColor );

                            }

 

                            <!-- Check box left -->

                            if (col &gt; 0)

                            {

                                checkBoxColor(  row, col - 1, selectedColor );

                            }

 

                            <!-- check box below -->

                            if (row &lt; (&MAX_ROWS; - 1))

                            {

                                checkBoxColor( row + 1, col, selectedColor );

                            }

 

                            <!-- check box to the right -->

                            if (col &lt; (&MAX_COLS; - 1))

                            {

                                checkBoxColor( row, col + 1, selectedColor );

                            }

                        }

 

                        <!-- Make sure the user selected a box that had neighbors of

                           the same color -->

                        if (numBoxesToBeRemoved &gt; 1)

                        {

                            <!-- user selected a valid box, remove the marked boxes after

                            a slight delay -->

                            <!-- to show the user some feedback on which boxes are being

                            deleted -->

                            sDelayedRemoveBoxes = true;

                        }

                        else

                        {

                            <!-- user selected a box that has no neighbors of the same

                            color so back out -->

                            mBoxes[ row ][ col ][ 0 ] = selectedColor;

                            mBoxes[ row ][ col ][ 1 ] = selectedInnerColor;

                            repaintBox( row, col );

                        }

                    }

                </script>

            </function>

            <function name="removeBoxes" type="void">

                <variables>

                    <var name="row" type="int"/>

                    <var name="col" type="int"/>

                    <var name="availRow" type="int"/>

                    <var name="topBox" type="int"/>

                    <var name="thereAreRemovableBoxes" type="boolean"/>

                </variables>

                <script>

                    <!-- hide cursor -->

                    sCursor = false;

                    mNumBoxesCleared = 0;

                    gAvailCol = &MAX_COLS;;

                    col = &MAX_COLS; - 1;

 

                    <!-- for each column, check to see if we've marked it as having boxes

                          to remove -->

                    while (col &gt; -1)

                    {

                        if (mRemoveBoxesFromColumn[ col ])

                        {

                        <!-- this column has boxes to be removed; remove them now -->

                            topBox = &MAX_ROWS; - mNumBoxesInColumn[ col ];

                            row = topBox;

 

                            while (row &lt; &MAX_ROWS;)

                            {

                                if (mBoxes[ row ][ col ][ 0 ] == &CLEARING_COLOR;)

                                {

                                    mNumBoxesCleared = mNumBoxesCleared + 1;

                                    mBoxes[ row ][ col ][ 0 ] = &CLEARED_COLOR;;

                                    mBoxes[ row ][ col ][ 1 ] = &CLEARED_COLOR;;

                                    repaintBox( row, col );

                                    availRow = row;

                                    mNumBoxesInColumn[col] = mNumBoxesInColumn[col] - 1;

                                }

 

                                row = row + 1;

                            }

 

                            <!-- remove boxes in column -->

                            row = &MAX_COLS; - 1;

                            row = availRow - 1;

                            topBox = topBox - 1;

 

                            while (row &gt; topBox)

                            {

                                if (mBoxes[ row ][ col ][ 0 ] != &CLEARED_COLOR;)

                                {

                                    mBoxes[ availRow ][col][0] = mBoxes[row][col][ 0 ];

                                    mBoxes[ availRow ][col][1] = mBoxes[row][col][ 1 ];

                                    mBoxes[ row ][ col ][ 0 ] = &CLEARED_COLOR;;

                                    mBoxes[ row ][ col ][ 1 ] = &CLEARED_COLOR;;

                                    repaintBox( row, col );

                                    repaintBox( availRow, col );

                                    availRow = availRow - 1;

                                }

 

                                row = row - 1;

                            }

                        }

 

                        mRemoveBoxesFromColumn[ col ] = false;

 

                        if (mNumBoxesInColumn[ col ] == 0)

                        {

                            gAvailCol = col;

                        }

 

                        col = col - 1;

                    }

 

                    <!-- if there are empty columns, remove them; if gAvailCol is

                  less than &MAX_COLS;, then -->

                    <!-- there is at least one empty column -->

                    gCol = gAvailCol;

                    sRemoveColumns = gCol &lt; &MAX_COLS;;

                </script>

            </function>

            <function name="moveColumn" type="void">

                <parameters>

                    <var name="fromCol" type="int"/>

                    <var name="toCol" type="int"/>

                </parameters>

                <variables>

                    <var name="row" type="int"/>

                </variables>

                <script>

                    while (row &lt; &MAX_ROWS;)

                    {

                        mBoxes[ row ][ toCol ][ 0 ] = mBoxes[ row ][ fromCol ][ 0 ];

                        mBoxes[ row ][ toCol ][ 1 ] = mBoxes[ row ][ fromCol ][ 1 ];

                        mBoxes[ row ][ fromCol ][ 0 ] = &CLEARED_COLOR;;

                        mBoxes[ row ][ fromCol ][ 1 ] = &CLEARED_COLOR;;

                        repaintBox( row, fromCol );

                        repaintBox( row, toCol );

                        row = row + 1;

                    }

 

                    mNumBoxesInColumn[ toCol ] = mNumBoxesInColumn[ fromCol ];

                    mNumBoxesInColumn[ fromCol ] = 0;

                </script>

            </function>

                                  …

        </functions>

        <templates>

            <template name="tBox">

                <display>

                    <box>

                        <x> <eval>tBoxCol * mBoxWidth</eval> </x>

                        <y> <eval>tBoxRow * mBoxHeight + mTextHeight</eval> </y>

                        <width> <eval>mBoxWidth</eval> </width>

                        <height> <eval>mBoxHeight</eval> </height>

                        <fg> <eval>mBoxes[ tBoxRow ][ tBoxCol ][ 1 ]</eval> </fg>

                        <bg> <eval>mBoxes[ tBoxRow ][ tBoxCol ][ 0 ]</eval> </bg>

                        <box>

                            <x> <eval>mBoxWidth / 4</eval> </x>

                            <y> <eval>mBoxHeight / 4</eval> </y>

                            <width> <eval>mBoxWidth / 2</eval> </width>

                            <height> <eval>mBoxHeight / 2</eval> </height>

                            <fg> <eval>mBoxes[ tBoxRow ][ tBoxCol ][ 1 ]</eval> </fg>

                            <bg> <eval>mBoxes[ tBoxRow ][ tBoxCol ][ 1 ]</eval> </bg>

                        </box>

                    </box>

                </display>

            </template>

        </templates>

        <script>

        …

        </script>

        <display>

        …

        </display>

        <states>

            <state var="sDelayedSelectBox">

                <transition value="true">

                    <delay>200</delay>

                    <script>

                        selectBox( mCursorRow, mCursorCol );

                        sDelayedSelectBox = false;

                    </script>

                </transition>

            </state>

            <state var="sBoxes" index="*,*">

                <transition value="true">

                    <display>

                        <expand template="tBox"/>

                    </display>

                </transition>

            </state>

            <state var="sCursor">

                <transition value="true">

                    <display>

                        <box>

                            <y> <eval>mTextHeight</eval> </y>

                            <width> <eval>mBoxWidth * &MAX_COLS;</eval> </width>

                            <height> <eval>mBoxHeight * &MAX_ROWS;</eval> </height>

                            <fg>&_COLOR_TRANSPARENT;</fg>

                            <bg>&_COLOR_TRANSPARENT;</bg>

                            <event name="onselect">

                                <script>

                                    setCursor( _getIntProperty( &_VALUE_INT_ONSELECT_Y_REL; ) / mBoxHeight,

                                               _getIntProperty( &_VALUE_INT_ONSELECT_X_REL; ) / mBoxWidth );

                                    sDelayedSelectBox = true;

                                </script>

                            </event>

                        </box>

                        <box>

                            <x> <eval>mCursorCol * mBoxWidth + mBoxWidth / 2 - 2</eval>

                            </x>

                            <y> <eval>(mCursorRow * mBoxHeight) + mTextHeight + 2</eval>

                            </y>

                            <width> <eval>4</eval> </width>

                            <height> <eval>mBoxHeight - 4</eval> </height>

                            <event name="onselect">

                                <accelerators> <key>FIRE</key> </accelerators>

                                <script>

                                    selectBox( mCursorRow, mCursorCol );

                                </script>

                            </event>

                        </box>

                        <box>

                            <x> <eval>mCursorCol * mBoxWidth + 2</eval> </x>

                            <y> <eval>(mCursorRow * mBoxHeight) + mTextHeight +

                                      mBoxHeight / 2 - 2</eval> </y>

                            <width> <eval>mBoxWidth - 4</eval> </width>

                            <height> <eval>4</eval> </height>

                            <event name="onselect">

                                <accelerators> <key>LEFT</key> </accelerators>

                                <script>

                                    setCursor( mCursorRow,

                                               (mCursorCol+&MAX_COLS;-1) % &MAX_COLS; );

                                </script>

                            </event>

                        </box>

                        <box>

                            <x> <eval>mCursorCol * mBoxWidth + mBoxWidth / 2 - 2</eval>

                            </x>

                            <y> <eval>(mCursorRow * mBoxHeight) + mTextHeight +

                                       mBoxHeight / 2 - 2</eval> </y>

                            <width> <eval>4</eval> </width>

                            <height> <eval>4</eval> </height>

                            <bg>

                                <eval>mBoxes[ mCursorRow ][ mCursorCol ][ 1 ]</eval>

                            </bg>

                            <event name="onselect">

                                <accelerators> <key>RIGHT</key> </accelerators>

                                <script>

                                    setCursor( mCursorRow, (mCursorCol + 1) % &MAX_COLS; );

                                </script>

                            </event>

                        </box>

                        <box>

                            <event name="onselect">

                                <accelerators> <key>UP</key> </accelerators>

                                <script>

                                    if (mCursorRow == 0)

                                    {

                                        <!-- move to bottom of previous column -->

                                        setCursor( &MAX_ROWS; - 1,

                                                  (mCursorCol + &MAX_COLS; - 1) %

                                                   &MAX_COLS; );

                                    }

                                    else

                                    {

                                        <!-- move to previous row -->

                                        setCursor( mCursorRow - 1, mCursorCol );

                                    }

                                </script>

                            </event>

                        </box>

                        <box>

                            <event name="onselect">

                                <accelerators> <key>DOWN</key> </accelerators>

                                <script>

                                    if (mCursorRow == (&MAX_ROWS; - 1))

                                    {

                                        <!-- move to top of next column -->

                                        setCursor( 0, (mCursorCol + 1) % &MAX_COLS; );

                                    }

                                    else

                                    {

                                        <!-- move to next row -->

                                        setCursor( mCursorRow + 1, mCursorCol );

                                    }

                                </script>

                            </event>

                        </box>

                        <box>

                            <x> <eval>mCursorCol * mBoxWidth</eval> </x>

                            <y> <eval>(mCursorRow * mBoxHeight) + mTextHeight</eval> </y>

                            <width> <eval>mBoxWidth</eval> </width>

                            <height> <eval>mBoxHeight</eval> </height>

                            <fg>&_COLOR_TRANSPARENT;</fg>

                            <bg>&_COLOR_TRANSPARENT;</bg>

                            <event name="onselect">

                                <accelerators> <key>FIRE</key> </accelerators>

                                <script>

                                    selectBox( mCursorRow, mCursorCol );

                                </script>

                            </event>

                        </box>

                    </display>

                </transition>

            </state>

            <state var="sDelayedRemoveBoxes">

                <transition value="true">

                    <delay>400</delay>

                    <script>

                        sDelayedRemoveBoxes = false;

                        removeBoxes();

                    </script>

                </transition>

            </state>

            <state var="sRemoveColumns">

                <transition value="true">

                    <delay>

                        <eval>gRemoveColumnsDelay</eval>

                    </delay>

                    <script>

                        gRemoveColumnsDelay = 0;

 

                        if (mNumBoxesInColumn[ gCol ] &gt; 0)

                        {

                            <!-- this column is not empty so move it to the empty

                                 available column -->

                            moveColumn( gCol, gAvailCol );

                            gAvailCol = gAvailCol + 1;

                            gRemoveColumnsDelay = 200;

                        }

 

                        gCol = gCol + 1;

                        <!-- reset self -->

                        _clear_state( sRemoveColumns );

                        sRemoveColumns = gCol &lt; &MAX_COLS;;

                    </script>

                </transition>

                <transition value="false">

                    <script>

                        sCursor = true;

                        <!-- reset self -->

                        gRemoveColumnsDelay = 0;

                        _clear_state( sRemoveColumns );

                    </script>

                </transition>

            </state>

            <state var="sStart">

              …

            </state>

        </states>

    </application>

</ujml>

            File: ujinn4.ujml

 

Now the user can clear groups:

 

 

The implementation of removing boxes is straightforward logic so it is left to the reader to view it in the source UJML in ujinn4.ujml. Some UJML specifics include further use of delayed states to provide animated user feedback as the group of boxes is removed and the columns are shifted to the left. Also, the program uses a simple stack which is implemented as a reusable UJML fragment; discussed in the next section.

Reusable Stack

Given that a central theme and goal of UJML is to allow developers to implement an application once and deploy it to many platforms, it follows that developing reusable UJML “components” is an important topic in the context of code reuse and redeployment.

 

Although the current version, 1.4, of UJML does not directly support the development of reusable components like Java classes and COM objects, it does allow the developer quite a bit of flexibility for reusing UJML efficiently using the <state-machine> element[5].

 

The <state-machine> and <include> elements are discussed later in this document.

 

Below is the <state-machine> that defines a simple reusable stack, as used by Ujinn.

 

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE ujml PUBLIC "-//UIEVOLUTION//DTD UJML 1.4//EN" "../ujml.dtd" [

    <!ENTITY % include SYSTEM "include.ujmc">

    %include;

]>

<ujml>

  <partition>

      <state-machine name="IntStack" access="export">

 

        <variables>

          <var name="mItems" type="int" size="&STACK_MAX_ITEMS;"/>

          <var name="mTop" type="int"/>

        </variables>

 

        <functions>

          <function name="push" type="int" visibility="public">

            <parameters>

              <var name="item" type="int"/>

            </parameters>

            <script>

              mItems[ mTop ] = item;

              mTop = mTop + 1;

            </script>

            <return>

              <eval>mTop</eval>

            </return>

          </function>

 

          <function name="pop" type="int" visibility="public">

            <variables>

              <var name="item" type="int"/>

            </variables>

            <script>

              if (mTop &gt; 0)

              {

                  mTop = mTop - 1;

                  item = mItems[ mTop ];

              }

            </script>

            <return>

              <eval>item</eval>

            </return>

          </function>

 

          <function name="size" type="int" visibility="public">

            <return>

              <eval>mTop</eval>

            </return>

          </function>

 

        </functions>

 

      </state-machine>

    </state-machines>

  </partition>

</ujml>

 

            File: ujlib/stack.ujml

Score Display

Now we add the ability to update the score at the top of the screen.

 

<ujml>

    <application>

        <state-variables>

            …

            <state-var name="sScores" type="boolean"/>

            <state-var name="sDelayedUpdateScores" type="boolean"/>

        </state-variables>

        <variables>

            …

            <var name="mScore" type="int"/>

            <var name="mHighScore" type="int"/>

            <var name="mTotalBoxesCleared" type="int"/>

            <var name="mLevelNumber" type="int"/>

            …

        </variables>

        <functions>

            …

            <function name="updateScores" type="void">

                <script>

                    sDelayedUpdateScores = true;

                </script>

            </function>

            …

            <function name="repaint" type="void">

                <variables>

                    <var name="col" type="int"/>

                </variables>

                <script>

                    while (col &lt; &MAX_COLS;)

                    {

                        repaintColumn( col );

                        col = col + 1;

                    }

                    updateScores();

                </script>

            </function>

            …

            <function name="setup" type="void">

                <script>

                    <!-- initialize global values based on the game grid size -->

                    mBoxWidth = mScrWidth / &MAX_COLS;;

                    mBoxHeight = (mScrHeight - mTextHeight) / &MAX_ROWS;;

                    mNumBoxes = &MAX_ROWS; * &MAX_COLS;;

                    <!-- reset scoring counters -->

                    mScore = 0;

                    mTotalBoxesCleared = 0;

                    mLevelNumber = 1;

                    <!-- move selected box to upper left corner -->

                    mCursorRow = 0;

                    mCursorCol = 0;

                    sCursor = false;

                    sCursor = true;

                </script>

            </function>

            <function name="clearScores" type="void">

                <script>

                    mScore = 0;

                    mHighScore = 0;

                    updateScores();

                </script>

            </function>

            …

        </functions>

            …

        <states>

            <state var="sDelayedUpdateScores">

                <transition value="true">

                    <delay>10</delay>

                    <script>

                        sScores = false;

                        sScores = true;

                        sDelayedUpdateScores = false;

                    </script>

                </transition>

            </state>

            <state var="sScores">

                <transition value="true">

                    <display>

                        <box>

                            <width> <eval>mScrWidth</eval> </width>

                            <height> <eval>mTextHeight</eval> </height>

                            <fg>&SCORE_ITEM_BG;</fg>

                            <bg>&SCORE_ITEM_BG;</bg>

                            <label>

                                <text>

                                    <strcat>

                                        <eval>mScore</eval>

                                        <val>/</val>

                                        <eval>mHighScore</eval>

                                        <val> | </val>

                                        <eval>mLevelNumber</eval>

                                        <val> | </val>

                                        <eval>mTotalBoxesCleared</eval>

                                        <val>/</val>

                                        <eval>mNumBoxes</eval>

                                        <val> |</val>

                                    </strcat>

                                </text>

                                <x>2</x>

                                <fg>&SCORE_ITEM_FG;</fg>

                            </label>

                        </box>

                    </display>

                </transition>

            </state>

            …

            <state var="sRemoveColumns">

                <transition value="true">

                    …

                </transition>

                <transition value="false">

                    <script>

                        <!-- update score -->

                        mScore = mScore + (mNumBoxesCleared * (mNumBoxesCleared - 1));

                        mTotalBoxesCleared = mTotalBoxesCleared + mNumBoxesCleared;

                        updateScores();

                        sCursor = true;

                        <!-- reset self -->

                        gRemoveColumnsDelay = 0;

                        _clear_state( sRemoveColumns );

                    </script>

                </transition>

            </state>

            …

        </states>

    </application>

</ujml>

            File: ujinn5.ujml

 

 

Notice that the function updateScores() simply sets the sDelayedUpdatesScores state variable to true. This state transition uses a <delay> element to defer the actual score update. This is done to avoid repainting two independent sections of the screen at the same time because doing so can dramatically slow down the screen repainting process on slow devices.

 

The UIE Player attempts to repaint the smallest part of the screen that has become invalid due to display states being added or removed from the state machine. The UIE Player coalesces invalid screen regions into one invalid region. So, if two areas of the screen are far from each other, the UIE Player will repaint the rectangular section of the screen that contains both regions; thus repainting much more than is necessary. By using delays, the application can force the two regions to be repainted independently and the resulting performance increase can be dramatic; especially for grid-based games like Ujinn where an overly-large invalid screen rectangle can cause many display states to be repainted.

 

Tip: Use the <delay> element to show spatially disjointed display states independently.

 

Endgame Checking

The changes below check for when the grid is either cleared completely or when there are no more groups to clear.

 

<ujml>

    <application>

        <state-variables>

            …

            <state-var name="sGameOver" type="boolean"/>

            <state-var name="sDelayedEndGameCheck" type="boolean"/>

            <state-var name="sMessage" type="boolean"/>

        </state-variables>

        …

        <states>

            …

            <state var="sRemoveColumns">

                <transition value="true">

                    …

                </transition>

                <transition value="false">

                    <script>

                        <!-- update score -->

                        mScore = mScore + (mNumBoxesCleared * (mNumBoxesCleared - 1));

                        mTotalBoxesCleared = mTotalBoxesCleared + mNumBoxesCleared;

                        <!-- Check to see if the end of the game has been reached -->

                        sDelayedEndGameCheck = true;

                        <!-- reset self -->

                        gRemoveColumnsDelay = 0;

                        _clear_state( sRemoveColumns );

                    </script>

                </transition>

            </state>

            <state var="sDelayedEndGameCheck">

                <transition value="true">

                    <delay>100</delay>

                    <variables>

                        <var name="fGameOver" type="boolean"/>

                    </variables>

                    <script>

                        <!-- see if the end of the game has been reached. Either all boxes have been removed -->

                        <!-- or it is not possible to remove any more boxes. -->

 

                        if (mTotalBoxesCleared == mNumBoxes)

                        {

                            <!-- we have a winner! all boxes have been removed. -->

                            mScore = mScore + (mScore / 4);

                            mMessage = _strcat( "Level cleared!

 Bonus: ", _strcat( mScore / 4, "." ) );

                            sMessage = true;

 

                            if (mScore &gt; mHighScore)

                            {

                                mHighScore = mScore;

                            }

 

                            fGameOver = true;

                        }

                        else

                        {

                            <!-- determine if there are removable boxes -->

 

                            if (!areBoxesRemovable())

                            {

                                <!-- there are no more selectable boxes so the game is over -->

                                mScore = mScore - (mNumBoxes - mTotalBoxesCleared);

                                mMessage = _strcat( "No more moves.

Penalty: ", _strcat( mNumBoxes - mTotalBoxesCleared, "." ) );

                                sMessage = true;

 

                                if (mScore &gt; mHighScore)

                                {

                                    mHighScore = mScore;

                                }

 

                                fGameOver = true;

                            }

                        }

 

                        updateScores();

                        <!-- repaint cursor -->

                        sCursor = false;

                        sCursor = true;

                        sDelayedEndGameCheck = false;

                        sGameOver = fGameOver;

                    </script>

                </transition>

            </state>

            <state var="sGameOver">

                <transition value="true">

                    <script>

                        sCursor = false;

                    </script>

                </transition>

            </state>

            <state var="sMessage">

                <transition value="true">

                    <display>

                        <multi-label>

                            <text><eval>mMessage</eval> </text>

                            <x>10</x>

                            <y>10</y>

                            <width><eval>mScrWidth - 20</eval> </width>

                            <height><eval>mScrHeight - 20</eval></height>

                            <bg>&_COLOR_WHITE;</bg>

                            <event name="onselect">

                                <accelerators> <key>FIRE</key> </accelerators>

                                <script>

                                    sMessage = false;

                                    start();

                                </script>

                            </event>

                        </multi-label>

                    </display>

                </transition>

            </state>

        </states>

    </application>

</ujml>

            File: ujinn6.ujml

 

Menus

At this point, the basic Ujinn game is complete. However, the game can be augmented by giving the user options to: play a different level, change the grid size, change the number of colors, or replay the same level.

 

We now add a main menu and an options editor dialog box to allow the user to specify the various options described above. Since menus are likely to be useful in other applications, we’ll implement them as a reusable UJML <state-machine> as defined below.

 

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE ujml PUBLIC "-//UIEVOLUTION//DTD UJML 1.4//EN" "../ujml.dtd" [

    <!ENTITY % include SYSTEM "include.ujmc">

    %include;

    <!ENTITY HIGHLIGHT_VISIBLE "-1">

    <!ENTITY HIGHLIGHT_FIRE_SELECT_EVENT "-2">

    <!ENTITY MENU_MAX_ITEMS "6">

]>

<ujml>

  <partition>

    <state-machines>

 

      <include file="include.ujml" state-machine="Event"/>

 

      <state-machine name="Menu" access="export">

 

        <state-variables>

          <state-var name="sMenu" type="boolean"/>

          <state-var name="sHighlight" type="int"/>

          <state-var name="sMenuItems" type="boolean" size="&MENU_MAX_ITEMS;"/>

        </state-variables>

 

        <variables>

          <var name="mHandler" type="string"/>

          <var name="mScrWidth" type="int"/>

          <var name="mScrHeight" type="int"/>

          <var name="mX" type="int"/>

          <var name="mY" type="int"/>

          <var name="mWidth" type="int"/>

          <var name="mItemHeight" type="int"/>

          <var name="mNumItems" type="int"/>

          <var name="mCaptions" type="string" size="&MENU_MAX_ITEMS;"/>

          <var name="mItemIndex" type="int"/>

          <var name="mCommands" type="int" size="&MENU_MAX_ITEMS;"/>

          <var name="mCurItemOffsetY" type="int"/>

          <var name="mCurItem" type="int"/>

          <var name="mAttributes" type="int" size="&MENU_ATTRIBUTES;"/>

        </variables>

 

        <functions>

          <function name="selectItem" type="void">

            <parameters>

              <var name="item" type="int"/>

            </parameters>

            <script>

              mCurItem = item;

              mCurItemOffsetY = mCurItem * mItemHeight;

   

              Event.setParam( 0, mCommands[ mCurItem ] );

              Event.fireEventNow( &EVENT_SELECTING;, mHandler );

   

              _clear_state( sHighlight );

              sHighlight = &HIGHLIGHT_FIRE_SELECT_EVENT;;

            </script>

          </function>

          <function name="Highlight_show" type="void">

            <script>

              _clear_state( sHighlight );

              sHighlight = &HIGHLIGHT_VISIBLE;;

            </script>

          </function>

          <function name="Highlight_move" type="void">

            <parameters>

              <var name="delta" type="int"/>

            </parameters>

            <script>

              mCurItem = (mCurItem + delta + mNumItems) % mNumItems;

              mCurItemOffsetY = mCurItem * mItemHeight;

 

              Event.setParam( 0, mCommands[ mCurItem ] );

              Event.fireEventNow( &EVENT_HIGHLIGHTING;, mHandler );

 

              Highlight_show();

 

              Event.setParam( 0, mCommands[ mCurItem ] );

              Event.fireEvent( &EVENT_HIGHLIGHTED;, mHandler);

            </script>

          </function>

          <function name="setAttribute" type="void" visibility="public">

            <parameters>

              <var name="attribute" type="int"/>

              <var name="color" type="int"/>

            </parameters>

            <script>

              mAttributes[ attribute ] = color;

            </script>

          </function>

          <function name="getAttribute" type="int" visibility="public">

            <parameters>

              <var name="attribute" type="int"/>

            </parameters>

            <variables>

              <var name="retval" type="int"/>

            </variables>

            <script>

              if (attribute &_GTE; 0)

              {

                retval = mAttributes[ attribute ];

              }

              else if (attribute == &MENU_ATTR_X;)

              {

                retval = mX - mAttributes[ &MENU_ATTR_BORDER_THICKNESS; ];

              }

              else if (attribute == &MENU_ATTR_Y;)

              {

                retval = mY - mAttributes[ &MENU_ATTR_BORDER_THICKNESS; ];

              }

              else if (attribute == &MENU_ATTR_WIDTH;)

              {

                retval = mWidth + mAttributes[ &MENU_ATTR_BORDER_THICKNESS; ] * 2;

              }

              else if (attribute == &MENU_ATTR_HEIGHT;)

              {

                retval = mItemHeight * mNumItems +

                         mAttributes[ &MENU_ATTR_BORDER_THICKNESS; ] * 2;

              }

            </script>

            <return>

              <eval>retval</eval>

            </return>

          </function>

          <function name="setVisible" type="void" visibility="public">

            <parameters>

              <var name="visible" type="boolean"/>

            </parameters>

            <variables>

              <var name="item" type="int"/>

            </variables>

            <script>

              _clear_state( sMenu );

   

              sMenu = visible;

   

              if (visible)

              {

                while (item &lt; mNumItems)

                {

              <!-- allow client to alter colors before paiting item -->

                  Event.setParam( 0, mCommands[ mCurItem ] );

                  Event.fireEventNow( &EVENT_PAINTING;, mHandler );

   

                  mItemIndex = item;

                  _clear_state( sMenuItems[ item ] );

                  sMenuItems[ item ] = true;

                  item = item + 1;

                }

     

                Highlight_show();

              }

              else

              {

                while (item &lt; mNumItems)

                {

                  _clear_state( sMenuItems[ item ] );

                  item = item + 1;

                }

      

                _clear_state( sHighlight );

              }

            </script>

          </function>

          <function name="getVisible" type="boolean" visibility="public">

            <return>

              <eval>sMenu</eval>

            </return>

          </function>

          <function name="center" type="void" visibility="public">

            <script>

              mX = (mScrWidth - mWidth) / 2;

              mY = (mScrHeight - mNumItems * mItemHeight) / 2;

            </script>

          </function>

          <function name="initialize" type="void" visibility="public">

            <parameters>

              <var name="handler" type="string"/>

            </parameters>

            <script>

              setVisible( false );

              mScrWidth = _getIntProperty( &_PROPERTY_INT_SCREEN_WIDTH; );

              mScrHeight = _getIntProperty( &_PROPERTY_INT_SCREEN_HEIGHT; );

              mHandler = handler;

              mWidth = 0;

              mItemHeight = _text_height( 0, 0, 0 );

              mNumItems = 0;

              mCurItemOffsetY = 0;

              mCurItem = 0;

              setAttribute( &MENU_ATTR_ITEM_FG;, &_COLOR_BLACK; );

              setAttribute( &MENU_ATTR_ITEM_BG;, &_COLOR_WHITE; );

              setAttribute( &MENU_ATTR_BORDER;, &_COLOR_GRAY; );

              setAttribute( &MENU_ATTR_BG;, &_COLOR_BLACK; );

              setAttribute( &MENU_ATTR_HIGHLIGHT_FG;, &_COLOR_WHITE; );

              setAttribute( &MENU_ATTR_HIGHLIGHT_BG;, 0xff9999ff );

              setAttribute( &MENU_ATTR_SELECTED_FG;, &_COLOR_WHITE; );

              setAttribute( &MENU_ATTR_SELECTED_BG;, 0xff6666cc );

              mX = 3;

              mY = mX;

              setAttribute( &MENU_ATTR_BORDER_THICKNESS;, mX );

            </script>

          </function>

          <function name="add" type="int" visibility="public">

            <parameters>

              <var name="caption" type="string"/>

              <var name="command" type="int"/>

            </parameters>

            <script>

              mCaptions[ mNumItems ] = caption;

              mCommands[ mNumItems ] = command;

              mNumItems = mNumItems + 1;

   

              mWidth = _max( mWidth, _text_width( caption, 0, 0, 0 ) + 8 );

            </script>

            <return>

              <eval>mNumItems - 1</eval>

            </return>

          </function>

          <function name="update" type="void" visibility="public">

            <parameters>

              <var name="id" type="int"/>

              <var name="caption" type="string"/>

              <var name="evt" type="int"/>

            </parameters>

            <execute>

              <set var="mCaptions">

                <index>

                  <eval>id</eval>

                </index>

                <ref var="caption"/>

              </set>

              <set var="mCommands">

                <index>

                  <eval>id</eval>

                </index>

                <eval>evt</eval>

              </set>

            </execute>

          </function>

          <function name="highlight" type="void" visibility="public">

            <parameters>

              <var name="id" type="int"/>

            </parameters>

            <script>

              mCurItem = id % mNumItems;

              mCurItemOffsetY = mCurItem * mItemHeight;

 

              Event.setParam( 0, mCommands[ mCurItem ] );

              Event.fireEventNow( &EVENT_HIGHLIGHTING;, mHandler );

   

              if (getVisible())

              {

                Highlight_show();

              }

   

              Event.setParam( 0, mCommands[ mCurItem ] );

              Event.fireEvent( &EVENT_HIGHLIGHTED;, mHandler );

            </script>

          </function>

          <function name="setPosition" type="void" visibility="public">

            <parameters>

              <var name="x" type="int"/>

              <var name="y" type="int"/>

            </parameters>

            <script>

              mX = x + mAttributes[ &MENU_ATTR_BORDER_THICKNESS; ];

              mY = y + mAttributes[ &MENU_ATTR_BORDER_THICKNESS; ];

            </script>

          </function>

        </functions>

   

 

        <templates>

          <template name="Item">

            <display>

              <label>

                <text>

                  <eval>mCaptions[ mItemIndex ]</eval>

                </text>

                <x>3</x>

                <fg>

                  <eval>mAttributes[ &MENU_ATTR_ITEM_FG; ]</eval>

                </fg>

              </label>

            </display>

          </template>

        </templates>

        <states>

          <state var="sMenu">

            <transition value="true">

              <display>

                <!-- catch all unwanted events, especially clicking outside the

                     boundary of the menu -->

                <box>

                  <width><eval>mScrWidth</eval></width>

                  <height><eval>mScrHeight</eval></height>

                  <fg>&_COLOR_TRANSPARENT;</fg>

                  <bg>&_COLOR_TRANSPARENT;</bg>

   

 

                  <event name="onselect">

                    <accelerators>

                      <key>FIRE</key>

                      <key>LEFT</key>

                      <key>RIGHT</key>

                      <key>UP</key>

                      <key>DOWN</key>

                      <key>0</key>

                      <key>1</key>

                      <key>2</key>

                      <key>3</key>

                      <key>4</key>

                      <key>5</key>

                      <key>6</key>

                      <key>7</key>

                      <key>8</key>

                      <key>9</key>

                    </accelerators>

                    <script>

                    </script>

                  </event>

                </box>

   

 

                <box>

                  <x>

                    <eval>mX - mAttributes[ &MENU_ATTR_BORDER_THICKNESS; ]</eval>

                  </x>

                  <y>

                    <eval>mY - mAttributes[ &MENU_ATTR_BORDER_THICKNESS; ]</eval>

                  </y>

                  <width>

                    <eval>mWidth + 2 * mAttributes[ &MENU_ATTR_BORDER_THICKNESS; ]</eval>

                  </width>

                  <height>

                    <eval>(mItemHeight * mNumItems) + 2 * mAttributes[ &MENU_ATTR_BORDER_THICKNESS; ]</eval>

                  </height>

                  <fg>

                    <eval>mAttributes[ &MENU_ATTR_BORDER; ]</eval>

                  </fg>

                  <bg>

                    <eval>mAttributes[ &MENU_ATTR_BG; ]</eval>

                  </bg>

                  <box>

                    <x>

                      <eval>mAttributes[ &MENU_ATTR_BORDER_THICKNESS; ]</eval>

                    </x>

                    <y>

                      <eval>mAttributes[ &MENU_ATTR_BORDER_THICKNESS; ]</eval>

                    </y>

                    <width>

                      <eval>mWidth</eval>

                    </width>

                    <height>

                      <eval>(mItemHeight * mNumItems)</eval>

                    </height>

                    <fg>

                      <eval>mAttributes[ &MENU_ATTR_ITEM_BG; ]</eval>

                    </fg>

                    <bg>

                      <eval>mAttributes[ &MENU_ATTR_ITEM_BG; ]</eval>

                    </bg>

                  </box>

                </box>

              </display>

            </transition>

          </state>

          <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->

          <state var="sMenuItems" index="*">

            <transition value="true">

              <display>

                <box>

                  <x>

                    <eval>mX</eval>

                  </x>

                  <y>

                    <eval>mY + _state_index() * mItemHeight</eval>

                  </y>

                  <width>

                    <eval>mWidth</eval>

                  </width>

                  <height>

                    <eval>mItemHeight</eval>

                  </height>

                  <fg>&_COLOR_TRANSPARENT;</fg>

                  <bg>

                    <eval>mAttributes[ &MENU_ATTR_ITEM_BG; ]</eval>

                  </bg>

   

 

                  <event name="onselect">

                    <script>

                      selectItem( _state_index() );

                    </script>

                  </event>

    

 

                  <expand template="Item"/>

                </box>

              </display>

            </transition>

          </state>

          <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->

          <state var="sHighlight">

            <transition value="&HIGHLIGHT_VISIBLE;">

              <display>

                <box>

                  <x>

                    <eval>mX</eval>

                  </x>

                  <y>

                    <eval>mY + mCurItemOffsetY</eval>

                  </y>

                  <width>

                    <eval>mWidth</eval>

                  </width>

                  <height>

                    <eval>mItemHeight</eval>

                  </height>

                  <fg>

                    <eval>mAttributes[ &MENU_ATTR_HIGHLIGHT_BG; ]</eval>

                  </fg>

                  <bg>

                    <eval>mAttributes[ &MENU_ATTR_HIGHLIGHT_BG; ]</eval>

                  </bg>

                  <event name="onselect">

                    <accelerators>

                      <key>FIRE</key>

                    </accelerators>

                    <execute>

                      <eval>selectItem( mCurItem )</eval>

                    </execute>

                  </event>

   

 

                  <label>

                    <text>

                      <eval>mCaptions[ mCurItem ]</eval>

                    </text>

                    <x>3</x>

                    <fg>

                      <eval>mAttributes[ &MENU_ATTR_HIGHLIGHT_FG; ]</eval>

                    </fg>

                  </label>

                </box>

                <box>

                  <event name="onselect">

                    <accelerators>

                      <key>DOWN</key>

                    </accelerators>

                    <execute>

                      <eval>Highlight_move( 1 )</eval>

                    </execute>

                  </event>

                </box>

                <box>

                  <event name="onselect">

                    <accelerators>

                      <key>UP</key>

                    </accelerators>

                    <execute>

                      <eval>Highlight_move( 0 - 1 )</eval>

                    </execute>

                  </event>

                </box>

                <box>

                  <event name="onselect">

                    <accelerators>

                      <key>

                        <eval>mCurItem + 1</eval>

                      </key>

                    </accelerators>

                    <execute>

                      <eval>selectItem( mCurItem )</eval>

                    </execute>

                  </event>

                </box>

              </display>

            </transition>

            <!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->

            <transition value="&HIGHLIGHT_FIRE_SELECT_EVENT;">

              <display>

                <box>

                  <x>

                    <eval>mX</eval>

                  </x>

                  <y>

                    <eval>mY + mCurItemOffsetY</eval>

                  </y>

                  <width>

                    <eval>mWidth</eval>

                  </width>

                  <height>

                    <eval>mItemHeight</eval>

                  </height>

                  <fg>

                    <eval>mAttributes[ &MENU_ATTR_SELECTED_BG; ]</eval>

                  </fg>

                  <bg>

                    <eval>mAttributes[ &MENU_ATTR_SELECTED_BG; ]</eval>

                  </bg>

                  <label>

                    <text>

                      <eval>mCaptions[ mCurItem ]</eval>

                    </text>

                    <x>3</x>

                    <fg>

                      <eval>mAttributes[ &MENU_ATTR_SELECTED_FG; ]</eval>

                    </fg>

                  </label>

                </box>

              </display>

              <delay>500</delay>

              <script>

                Event.setParam( 0, mCommands[ mCurItem ] );

                Event.fireEvent( &EVENT_SELECTED;, mHandler );

              </script>

            </transition>

          </state>

 

        </states>

      </state-machine>

    </state-machines>

  </partition>

</ujml>

            File: menu.ujml

 

 

Now, let’s incorporate the menu into Ujinn.

 

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE ujml PUBLIC "-//UIEVOLUTION//DTD UJML 1.4//EN" "ujml.dtd" [

  <!ENTITY % include SYSTEM "ujlib/include.ujmc">

  %include;

  <!-- Colors -->

  <!ENTITY SCORE_ITEM_FG "&_COLOR_WHITE;">

  <!ENTITY SCORE_ITEM_BG "0xff000066">

  <!-- Limits -->

  <!ENTITY MAX_ROWS "8">

  <!ENTITY MAX_COLS "8">

  <!ENTITY MAX_COLORS "16">

  <!ENTITY CLEARING_COLOR "&_COLOR_WHITE;">

  <!ENTITY CLEARED_COLOR "&_COLOR_TRANSPARENT;">

  <!-- Popup -->

  <!ENTITY POPUP_HIDDEN "-1">

  <!ENTITY POPUP_NULL "0">

  <!ENTITY POPUP_MAIN_MENU "1">

  <!ENTITY POPUP_MESSAGE "4">

  <!-- Main Menu -->

  <!ENTITY MENU_NEW "Next Level">

  <!ENTITY MENU_RESTART "Replay Level">

  <!ENTITY MENU_DONE "Done">

  <!-- Events events -->

  <!ENTITY ON_START "1">

  <!ENTITY ON_NEXT_LEVEL "2">

  <!ENTITY ON_GOTO_MENU "3">

  <!ENTITY ON_NULL "4">

  <!ENTITY ON_RESTART "5">

]>

<ujml>

  <application>

    <state-machines>

      <include file="ujlib/stack.ujml" state-machine="IntStack"/>

      <include file="ujlib/event.ujml" state-machine="Event"/>

    </state-machines>

 

    <state-variables>

      <state-var name="sFnKeys" type="boolean"/>

      <state-var name="sBoxes" type="boolean" size="&MAX_ROWS;,&MAX_COLS;"/>

      <state-var name="sCursor" type="boolean"/>

      <state-var name="sPopup" type="int"/>

      <state-var name="sScores" type="boolean"/>

      <state-var name="sDelayedUpdateScores" type="boolean"/>

      <state-var name="sDelayedRemoveBoxes" type="boolean"/>

      <state-var name="sDelayedSelectBox" type="boolean"/>

      <state-var name="sRemoveColumns" type="boolean"/>

      <state-var name="sStart" type="boolean"/>

      <state-var name="sRestart" type="boolean"/>

      <state-var name="sGameOver" type="boolean"/>

      <state-var name="sDelayedEndGameCheck" type="boolean"/>

    </state-variables>

    <variables>

      <var name="mTextHeight" type="int"/>

      <var name="mScrHeight" type="int"/>

      <var name="mScrWidth" type="int"/>

      <var name="mBoxHeight" type="int"/>

      <var name="mBoxWidth" type="int"/>

      <var name="mColorMap" type="int" size="&MAX_COLORS;,2"/>

      <var name="mRemoveBoxesFromColumn" type="boolean" size="&MAX_COLS;"/>

      <var name="tBoxRow" type="int"/>

      <var name="tBoxCol" type="int"/>

      <var name="mMessage" type="string"/>

      <var name="mColorMapIndex" type="int"/>

      <var name="mColorMapBoxSize" type="int"/>

      <!-- persistent variables -->

      <var name="mNumRows" type="int"/>

      <var name="mNumCols" type="int"/>

      <var name="mNumColors" type="int"/>

      <var name="mScore" type="int"/>

      <var name="mHighScore" type="int"/>

      <var name="mTotalBoxesCleared" type="int"/>

      <var name="mLevelNumber" type="int"/>

      <var name="mCursorCol" type="int"/>

      <var name="mCursorRow" type="int"/>

      <var name="mNumBoxes" type="int"/>

      <var name="mNumBoxesInColumn" type="int" size="&MAX_COLS;"/>

      <!-- game board -->

      <var name="mBoxes" type="int" size="&MAX_ROWS;,&MAX_COLS;,2"/>

      <var name="mColumnHasRemovableBoxes" type="boolean" size="&MAX_COLS;"/>

      <!-- mSavedXXX variables are used to restart the current game -->

      <var name="mSavedBoxes" type="int" size="&MAX_ROWS;,&MAX_COLS;,2"/>

      <var name="mSavedColumnHasRemovableBoxes" type="boolean" size="&MAX_COLS;"/>

      <!-- end of persistent variables -->

      <!-- Globals used for removing columns -->

      <var name="mNumBoxesCleared" type="int"/>

      <var name="gI" type="int"/>

      <var name="gCol" type="int"/>

      <var name="gRow" type="int"/>

      <var name="gSeed" type="int"/>

      <var name="gAvailCol" type="int"/>

      <var name="gRemoveColumnsDelay" type="int"/>

    </variables>

    <functions>

      <function name="repaintPopup" type="void">

        <variables>

          <var name="temp" type="int"/>

        </variables>

        <script>

          temp = sPopup;

          sPopup = &POPUP_HIDDEN;;

          sPopup = temp;

        </script>

      </function>

       …

    </functions>

    <templates>

       …

    </templates>

    <script>

       …

    </script>

    <display>

       …

    </display>

    <states>

      <state var="sFnKeys">

        <transition value="true">

          <display>

            <fn>

              <text>Options</text>

              <event name="onselect">

                <accelerators>

                  <key>F2</key>

                  <key>POUND</key>

                  <key>STAR</key>

                </accelerators>

                <variables>

                  <var name="temp" type="int"/>

                </variables>

                <script>

                  temp = sPopup;

                  sPopup = &POPUP_HIDDEN;;

 

                  if (temp == &POPUP_MAIN_MENU;)

                  {

                    sPopup = &POPUP_NULL;;

                  }

                  else if (temp == &POPUP_NULL;)

                  {

                    sPopup = &POPUP_MAIN_MENU;;

                  }

                  else

                  {

                  <!-- an options menu is being displayed; restart the game with new settings -->

                    start();

                  }

                </script>

              </event>

            </fn>

          </display>

        </transition>

      </state>

       …

      <state var="sPopup">

        <transition value="&POPUP_NULL;">

          <script>

            Menu.setVisible( false );

          </script>

        </transition>

        <transition value="&POPUP_MAIN_MENU;">

          <script>

            Menu.initialize( "menu" );

            Menu.add( "&MENU_NEW;", &ON_NEXT_LEVEL; );

            Menu.add( "&MENU_RESTART;", &ON_RESTART; );

            Menu.add( "&MENU_DONE;", &ON_NULL; );

            Menu.center();

            Menu.setVisible( true );

          </script>

        </transition>

        <transition value="&POPUP_MESSAGE;">

          <display>

            <box>

              <x>

                <eval>2</eval>

              </x>

              <y>

                <eval>2</eval>

              </y>

              <width>

                <eval>mScrWidth - 4</eval>

              </width>

              <height>

                <eval>mScrHeight - 4</eval>

              </height>

              <bg>&_COLOR_DARKGRAY;</bg>

              <multi-label>

                <text>

                  <ref var="mMessage"/>

                </text>

                <x>2</x>

                <y>2</y>

                <width>

                  <eval>mScrWidth - 8</eval>

                </width>

                <height>

                  <eval>_max( mScrHeight - (mTextHeight * 2) - 4, mTextHeight * 2 )</eval>

                </height>

                <fg>&_COLOR_WHITE;</fg>

                <bg>&_COLOR_DARKGRAY;</bg>

              </multi-label>

            </box>

          </display>

          <script>

            Menu.initialize( "menu" );

            Menu.add( "&MENU_RESTART;", &ON_RESTART; );

            Menu.add( "&MENU_NEW;", &ON_NEXT_LEVEL; );

            Menu.center();

            Menu.setVisible( true );

          </script>

        </transition>

      </state>

 

      <state var="Event.sEvent" index="&EVENT_SELECTED;">

        <transition value="menu">

          <variables>

            <var name="command" type="int"/>

          </variables>

          <script>

            Menu.setVisible( false );

 

            command = Event.getParam( 0 );

       

            if (command == &ON_GOTO_MENU;)

            {

              sPopup = &POPUP_MAIN_MENU;;

            }

            else if (command == &ON_START;)

            {

              start();

            }

            else if (command == &ON_RESTART;)

            {

              restart();

            }

            else if (command == &ON_NEXT_LEVEL;)

            {

              mScore = 0;

              mHighScore = 0;

              mLevelNumber = mLevelNumber + 1;

              start();

            }

            else if (command == &ON_NULL;)

            {

              sPopup = &POPUP_NULL;;

            }

          </script>

        </transition>

      </state>

       …

      <state var="sDelayedEndGameCheck">

        <transition value="true">

          <delay>100</delay>

          <variables>

            <var name="fGameOver" type="boolean"/>

          </variables>

          <script>

            <!-- see if the end of the game has been reached. Either all boxes have been removed -->

            <!-- or it is not possible to remove any more boxes. -->

 

            if (mTotalBoxesCleared == mNumBoxes)

            {

            <!-- we have a winner! all boxes have been removed. -->

              mScore = mScore + (mScore / 4);

              mMessage = _strcat( "Level cleared!

Bonus: ", _strcat( mScore / 4, "." ) );

              sPopup = &POPUP_MESSAGE;;

 

              if (mScore &gt; mHighScore)

              {

                mHighScore = mScore;

              }

 

              fGameOver = true;

            }

            else

            {

            <!-- determine if there are removable boxes -->

 

              if (!areBoxesRemovable())

              {

            <!-- there are no more selectable boxes so the game is over -->

                mScore = mScore - (mNumBoxes - mTotalBoxesCleared);

                mMessage = _strcat( "No moves.

Penalty: ", _strcat( mNumBoxes - mTotalBoxesCleared, "." ) );

                sPopup = &POPUP_MESSAGE;;

 

                if (mScore &gt; mHighScore)

                {

                  mHighScore = mScore;

                }

 

                fGameOver = true;

              }

            }

 

            updateScores();

            <!-- repaint cursor -->

            sCursor = false;

            sCursor = true;

            sDelayedEndGameCheck = false;

            sGameOver = fGameOver;

          </script>

        </transition>

      </state>

       …

    </states>

       <!—menu goes on top of z-order ŕ

    <state-machines>

      <include file="ujlib/menu.ujml" state-machine="Menu"/>

    </state-machines>

  </application>

</ujml>

 

 

            File: ujinn7.ujml

 

   


   

 

Busy Cursor

On slow devices, the screen repaint at the start of a level can take a while so we’ll add a busy cursor to be displayed when the application is likely to take a few seconds to perform a task.

 

Since the busy cursor will likely be used in other applications, we’ll implement it as a reusable <state-machine>.

 

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE ujml PUBLIC "-//UIEVOLUTION//DTD UJML 1.4//EN" "../ujml.dtd" [

  <!ENTITY % include SYSTEM "include.ujmc">

  %include;

 

  <!ENTITY BUSY_FACE "0xff0000ff">

  <!ENTITY BUSY_HANDS "0xff00ffff">

  <!ENTITY BUSY_BG "0xfff000066">

  <!ENTITY BUSY_BORDER "2">

  <!ENTITY BUSY_HAND_THICKNESS "4">

  <!ENTITY BUSY_ANIMATE_FRAME_CNT "4">

]>

<ujml>

  <partition>

    <state-machines>

 

      <include file="include.ujml" state-machine="Linker"/>

 

      <state-machine name="Busy" access="export">

 

        <state-variables>

          <state-var name="sAnimate" type="boolean"/>

          <state-var name="sAnimateTick" type="boolean"/>

        </state-variables>

 

        <variables>

          <var name="mScrWidth" type="int"/>

          <var name="mScrHeight" type="int"/>

          <var name="mTextHeight" type="int"/>

          <var name="mAnimateFrame" type="int"/>

          <var name="mSize" type="int"/>

          <var name="mX" type="int"/>

          <var name="mY" type="int"/>

          <var name="mLittleHandX" type="int" size="&BUSY_ANIMATE_FRAME_CNT;"/>

          <var name="mLittleHandY" type="int" size="&BUSY_ANIMATE_FRAME_CNT;"/>

          <var name="mLittleHandWidth" type="int" size="&BUSY_ANIMATE_FRAME_CNT;"/>

          <var name="mLittleHandHeight" type="int" size="&BUSY_ANIMATE_FRAME_CNT;"/>

          <var name="mBigHandX" type="int" size="&BUSY_ANIMATE_FRAME_CNT;"/>

          <var name="mBigHandY" type="int" size="&BUSY_ANIMATE_FRAME_CNT;"/>

          <var name="mBigHandWidth" type="int" size="&BUSY_ANIMATE_FRAME_CNT;"/>

          <var name="mBigHandHeight" type="int" size="&BUSY_ANIMATE_FRAME_CNT;"/>

        </variables>

 

        <functions>

          <function name="centerText" type="int">

            <parameters>

              <var name="text" type="string"/>

            </parameters>

            <return>

              <eval>(mScrWidth - _text_width( text, 0, 0, 0 )) / 2</eval>

            </return>

          </function>

 

          <function name="initialize" type="void" visibility="public">

            <script>

              mScrWidth = _getIntProperty( &_PROPERTY_INT_SCREEN_WIDTH; );

              mScrHeight = _getIntProperty( &_PROPERTY_INT_SCREEN_HEIGHT; );

              mTextHeight = _text_height( 0, 0, 0 );

              mSize = _max( _min( mScrWidth, mScrHeight ) / &BUSY_HAND_THICKNESS;, 40 );

              mX = (mScrWidth - mSize) / 2;

              mY = (mScrHeight - mSize) / 2;

              mLittleHandX[ 0 ] = mX + mSize / 2 - &BUSY_HAND_THICKNESS; / 2;

              mLittleHandY[ 0 ] = mY + mSize / 2;

              mLittleHandWidth[ 0 ] = mSize / 2 - &BUSY_HAND_THICKNESS; * 2;

              mLittleHandHeight[ 0 ] = &BUSY_HAND_THICKNESS;;

              mBigHandX[ 0 ] = mX + mSize / 2 - &BUSY_HAND_THICKNESS; / 2;

              mBigHandY[ 0 ] = mY + &BUSY_HAND_THICKNESS;;

              mBigHandWidth[ 0 ] = &BUSY_HAND_THICKNESS;;

              mBigHandHeight[ 0 ] = mSize / 2;

              mLittleHandX[ 1 ] = mX + mSize / 2 - &BUSY_HAND_THICKNESS; / 2;

              mLittleHandY[ 1 ] = mY + mSize / 2;

              mLittleHandWidth[ 1 ] = mSize / 2 - &BUSY_HAND_THICKNESS; * 2;

              mLittleHandHeight[ 1 ] = &BUSY_HAND_THICKNESS;;

              mBigHandX[ 1 ] = mX + mSize / 2 - &BUSY_HAND_THICKNESS; / 2;

              mBigHandY[ 1 ] = mY + mSize / 2;

              mBigHandWidth[ 1 ] = mSize / 2 - &BUSY_HAND_THICKNESS;;

              mBigHandHeight[ 1 ] = &BUSY_HAND_THICKNESS;;

              mLittleHandX[ 2 ] = mX + mSize / 2 - &BUSY_HAND_THICKNESS; / 2;

              mLittleHandY[ 2 ] = mY + mSize / 2;

              mLittleHandWidth[ 2 ] = mSize / 2 - &BUSY_HAND_THICKNESS; * 2;

              mLittleHandHeight[ 2 ] = &BUSY_HAND_THICKNESS;;

              mBigHandX[ 2 ] = mX + mSize / 2 - &BUSY_HAND_THICKNESS; / 2;

              mBigHandY[ 2 ] = mY + mSize / 2;

              mBigHandWidth[ 2 ] = &BUSY_HAND_THICKNESS;;

              mBigHandHeight[ 2 ] = mSize / 2 - &BUSY_HAND_THICKNESS;;

              mLittleHandX[ 3 ] = mX + mSize / 2 - &BUSY_HAND_THICKNESS; / 2;

              mLittleHandY[ 3 ] = mY + mSize / 2;

              mLittleHandWidth[ 3 ] = mSize / 2 - &BUSY_HAND_THICKNESS; * 2;

              mLittleHandHeight[ 3 ] = &BUSY_HAND_THICKNESS;;

              mBigHandX[ 3 ] = mX + &BUSY_HAND_THICKNESS;;

              mBigHandY[ 3 ] = mY + mSize / 2;

              mBigHandWidth[ 3 ] = mSize / 2 - &BUSY_HAND_THICKNESS;;

              mBigHandHeight[ 3 ] = &BUSY_HAND_THICKNESS;;

            </script>

          </function>

 

          <function name="show" type="void" visibility="public">

            <script>

              mAnimateFrame = 0;

              sAnimate = true;

            </script>

          </function>

 

 

 

          <function name="hide" type="void" visibility="public">

            <script>

              sAnimate = false;

            </script>

          </function>

 

        </functions>

 

        <states>

          <state var="sAnimate">

            <transition value="true">

              <display>

                <box>

                  <x>

                    <eval>mX - &BUSY_BORDER;</eval>

                  </x>

                  <y>

                    <eval>mY - &BUSY_BORDER;</eval>

                  </y>

                  <width>

                    <eval>mSize + &BUSY_BORDER; * 2</eval>

                  </width>

                  <height>

                    <eval>mSize + &BUSY_BORDER; * 2</eval>

                  </height>

                  <fg>&BUSY_BG;</fg>

                  <bg>&BUSY_BG;</bg>

                  <oval>

                    <x>

                      <eval>&BUSY_BORDER;</eval>

                    </x>

                    <y>

                      <eval>&BUSY_BORDER;</eval>

                    </y>

                    <width>

                      <eval>mSize</eval>

                    </width>

                    <height>

                      <eval>mSize</eval>

                    </height>

                    <fg>&BUSY_FACE;</fg>

                    <bg>&BUSY_FACE;</bg>

                  </oval>

                </box>

              </display>

              <script>

                sAnimateTick = true;

              </script>

            </transition>

 

            <transition value="false">

              <script>

                sAnimateTick = false;

              </script>

            </transition>

          </state>

 

          <state var="sAnimateTick">

            <transition value="true">

              <display>

                <box>

                  <x>

                    <eval>mLittleHandX[ mAnimateFrame ]</eval>

                  </x>

                  <y>

                    <eval>mLittleHandY[ mAnimateFrame ]</eval>

                  </y>

                  <width>

                    <eval>mLittleHandWidth[ mAnimateFrame ]</eval>

                  </width>

                  <height>

                    <eval>mLittleHandHeight[ mAnimateFrame ]</eval>

                  </height>

                  <fg>&BUSY_HANDS;</fg>

                  <bg>&BUSY_HANDS;</bg>

                </box>

                <box>

                  <x>

                    <eval>mBigHandX[ mAnimateFrame ]</eval>

                  </x>

                  <y>

                    <eval>mBigHandY[ mAnimateFrame ]</eval>

                  </y>

                  <width>

                    <eval>mBigHandWidth[ mAnimateFrame ]</eval>

                  </width>

                  <height>

                    <eval>mBigHandHeight[ mAnimateFrame ]</eval>

                  </height>

                  <fg>&BUSY_HANDS;</fg>

                  <bg>&BUSY_HANDS;</bg>

                </box>

              </display>

              <delay>500</delay>

              <script>

                mAnimateFrame = (mAnimateFrame + 1) % &BUSY_ANIMATE_FRAME_CNT;;

                _clear_state( sAnimateTick );

                sAnimateTick = sAnimate;

              </script>

            </transition>

          </state>

        </states>

      </state-machine>

    </state-machines>

  </partition>

</ujml>

            File: busy.ujml

 

Below, we incorporate the busy cursor into Ujinn. Note that since there is already some animation happening on the screen, we will not use the animation effect of the busy cursor.

 

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE ujml PUBLIC "-//UIEVOLUTION//DTD UJML 1.4//EN" "ujml.dtd" [

  <!ENTITY % include SYSTEM "ujlib/include.ujmc">

  %include;

  ...

  <!ENTITY POPUP_BUSY "5">

  ...

]>

<ujml>

  <application>

    <state-machines>

      ...

    </state-machines>

 

    <state-variables>

      ...

    </state-variables>

    <variables>

      ...

    </variables>

    <functions>

      ...

      <function name="start" type="void">

        <script>

          sPopup = &POPUP_BUSY;;

          gSeed = _srand( mLevelNumber * 101 );

          gCol = 0;

          gRow = 0;

          sStart = true;

        </script>

      </function>

      <function name="restart" type="void">

        <script>

          sPopup = &POPUP_BUSY;;

          gCol = 0;

          gRow = 0;

          sRestart = true;

        </script>

      </function>

      ...

    </functions>

    <templates>

      ...

    </templates>

    <script>

      ...

      Busy.initialize();

      start();

    </script>

    <display>

      ...

    </display>

    <states>

      ...

      <state var="sPopup">

        <transition value="&POPUP_NULL;">

          <script>

            Busy.hide();

            Menu.setVisible( false );

          </script>

        </transition>

        <transition value="&POPUP_BUSY;">

          <script>

            Busy.show();

            sFnKeys = false;

            sCursor = false;

            Menu.setVisible( false );

          </script>

        </transition>

      ...

      </state>

      ...

    </states>

    <state-machines>

      <include file="ujlib/menu.ujml" state-machine="Menu"/>

      <include file="ujlib/busy.ujml" state-machine="Busy"/>

    </state-machines>

  </application>

</ujml>

 

            File: ujinn8.ujml

 

   

Resuming a Game

As a general rule, an application should save its state. If a user exits the application and later returns to the application, it can appear in the same state as when the user exited. This also applies when the device is turned off, or closed in the case of flip phones; many flip phones terminate the application when they are closed.

 

In Ujinn, the game state is saved using a property bag. When the game is resumed, it uses the property bag to reinitialize internal variables so that it returns to the same state as when it was exited.

 

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE ujml PUBLIC "-//UIEVOLUTION//DTD UJML 1.4//EN" "ujml.dtd" [

  ...

  <!-- persistent properties -->

  <!ENTITY PROP_BAG_NAME "Ujinn.Properties">

  <!ENTITY PROP_VERSION "1">

  <!ENTITY PROP_VERSION_MINOR "0">

  <!ENTITY PROP_STATUS_SAVED "10">

  <!ENTITY PROP_STATUS_NOT_SAVED "11">

  <!-- persistent property keys -->

  <!ENTITY PROP_KEY_VERSION "0">

  <!ENTITY PROP_KEY_STATUS "1">

  <!ENTITY PROP_KEY_VERSION_MINOR "2">

  <!ENTITY PROP_KEY_mNumRows "3">

  <!ENTITY PROP_KEY_mNumCols "4">

  <!ENTITY PROP_KEY_mNumColors "5">

  <!ENTITY PROP_KEY_mScore "6">

  <!ENTITY PROP_KEY_mHighScore "7">

  <!ENTITY PROP_KEY_mTotalBoxesCleared "8">

  <!ENTITY PROP_KEY_mLevelNumber "10">

  <!ENTITY PROP_KEY_mCursorCol "11">

  <!ENTITY PROP_KEY_mCursorRow "12">

  <!ENTITY PROP_KEY_sGameOver "13">

  <!ENTITY PROP_KEY_mNumBoxesInColumn "">

  <!ENTITY PROP_KEY_mColumnHasRemovableBoxes "">

  <!ENTITY PROP_KEY_mSavedColumnHasRemovableBoxes "">

  <!ENTITY PROP_KEY_mBoxes "17">

  <!ENTITY PROP_KEY_mSavedBoxes "">

]>

<ujml>

  <application>

    <state-machines>

      ...

    </state-machines>

 

    <state-variables>

      ...

      <state-var name="sStartup" type="boolean"/>

      <state-var name="sUnload" type="boolean"/>

      <state-var name="sResume" type="boolean"/>

      <state-var name="sRestart" type="boolean"/>

    </state-variables>

    <variables>

      ...

    </variables>

    <functions>

      ...

      <function name="loadBag" type="boolean">

          <parameters>

              <var name="bag" type="string"/>

          </parameters>

          <variables>

              <var name="rc" type="boolean"/>

          </variables>

          <script>

          if (_isSupported( &_X_PROPERTY_BAG; ))

          {

              rc = _x_loadBag( bag );

          }

          </script>

          <return>

              <eval>rc</eval>

          </return>

      </function>

      <function name="canResumeGame" type="boolean">

          <variables>

              <var name="rc" type="boolean"/>

              <var name="s" type="string"/>

              <var name="i" type="int"/>

              <var name="b" type="boolean"/>

              <var name="fSaved" type="boolean"/>

          </variables>

          <script>

              if (loadBag( "&PROP_BAG_NAME;" ))

              {

              <!-- get the version from the bag -->

                  i = _x_getBagInt( "&PROP_BAG_NAME;", &PROP_KEY_VERSION;, -1 );

 

                  if (i &lt; 1)

                  {

              <!-- this is a new bag so initialize it -->

 

                      _x_setBagInt( "&PROP_BAG_NAME;", &PROP_KEY_VERSION;, &PROP_VERSION; );

                      _x_setBagInt( "&PROP_BAG_NAME;", &PROP_KEY_VERSION_MINOR;, &PROP_VERSION_MINOR; );

                      _x_setBagInt( "&PROP_BAG_NAME;", &PROP_KEY_STATUS;, &PROP_STATUS_NOT_SAVED; );

                      _x_storeBag( "&PROP_BAG_NAME;" );

                  }

                  else

                  {

                      fSaved = ( _x_getBagInt( "&PROP_BAG_NAME;", &PROP_KEY_STATUS;, -1 ) == &PROP_STATUS_SAVED;);

                  }

 

                  _x_closeBag( "&PROP_BAG_NAME;" );

              }

          </script>

          <return>

              <eval>fSaved</eval>

          </return>

      </function>

      <function name="saveGame" type="void">

          <variables>

              <var name="row" type="int"/>

              <var name="col" type="int"/>

              <var name="i" type="int"/>

          </variables>

          <script>

              if (loadBag( "&PROP_BAG_NAME;" ))

              {

                  _x_setBagInt( "&PROP_BAG_NAME;", &PROP_KEY_STATUS;, &PROP_STATUS_NOT_SAVED; );

                  _x_setBagInt( "&PROP_BAG_NAME;", &PROP_KEY_mNumRows;, mNumRows );

                  _x_setBagInt( "&PROP_BAG_NAME;", &PROP_KEY_mNumCols;, mNumCols );

                  _x_setBagInt( "&PROP_BAG_NAME;", &PROP_KEY_mNumColors;, mNumColors );

                  _x_setBagInt( "&PROP_BAG_NAME;", &PROP_KEY_mScore;, mScore );

                  _x_setBagInt( "&PROP_BAG_NAME;", &PROP_KEY_mHighScore;, mHighScore );

                  _x_setBagInt( "&PROP_BAG_NAME;", &PROP_KEY_mTotalBoxesCleared;, mTotalBoxesCleared );

                  _x_setBagInt( "&PROP_BAG_NAME;", &PROP_KEY_mLevelNumber;, mLevelNumber );

                  _x_setBagInt( "&PROP_BAG_NAME;", &PROP_KEY_mCursorCol;, mCursorCol );

                  _x_setBagInt( "&PROP_BAG_NAME;", &PROP_KEY_mCursorRow;, mCursorRow );

                  _x_setBagBoolean( "&PROP_BAG_NAME;", &PROP_KEY_sGameOver;, sGameOver );

 

                  i = &PROP_KEY_sGameOver; + 1;

 

                  while (col &lt; &MAX_COLS;)

                  {

                      _x_setBagInt( "&PROP_BAG_NAME;", i, mNumBoxesInColumn[ col ] );

                      i = i + 1;

                      _x_setBagBoolean( "&PROP_BAG_NAME;", i, mColumnHasRemovableBoxes[ col ] );

                      i = i + 1;

                      _x_setBagBoolean( "&PROP_BAG_NAME;", i, mSavedColumnHasRemovableBoxes[ col ] );

                      i = i + 1;

 

                      row = 0;

 

                      while (row &lt; &MAX_ROWS;)

                      {

                          _x_setBagInt( "&PROP_BAG_NAME;", i, mBoxes[ row ][ col ][ 0 ] );

                          i = i + 1;

                          _x_setBagInt( "&PROP_BAG_NAME;", i, mBoxes[ row ][ col ][ 1 ] );

                          i = i + 1;

                          _x_setBagInt( "&PROP_BAG_NAME;", i, mSavedBoxes[ row ][ col ][ 0 ] );

                          i = i + 1;

                          _x_setBagInt( "&PROP_BAG_NAME;", i, mSavedBoxes[ row ][ col ][ 1 ] );

                          i = i + 1;

 

                          row = row + 1;

                      }

 

                      col = col + 1;

                  }

 

              <!-- save the bag -->

                  _x_setBagInt( "&PROP_BAG_NAME;", &PROP_KEY_STATUS;, &PROP_STATUS_SAVED; );

                  _x_storeBag( "&PROP_BAG_NAME;" );

                  _x_closeBag( "&PROP_BAG_NAME;" );

              }

          </script>

      </function>

      <function name="resumeGame" type="void">

          <variables>

              <var name="row" type="int"/>

              <var name="col" type="int"/>

              <var name="i" type="int"/>

          </variables>

          <script>

              if (loadBag( "&PROP_BAG_NAME;" ))

              {

                  mNumRows = _x_getBagInt( "&PROP_BAG_NAME;", &PROP_KEY_mNumRows; );

                  mNumCols = _x_getBagInt( "&PROP_BAG_NAME;", &PROP_KEY_mNumCols; );

                  mNumColors = _x_getBagInt( "&PROP_BAG_NAME;", &PROP_KEY_mNumColors; );

                  mScore = _x_getBagInt( "&PROP_BAG_NAME;", &PROP_KEY_mScore; );

                  mHighScore = _x_getBagInt( "&PROP_BAG_NAME;", &PROP_KEY_mHighScore; );

                  mTotalBoxesCleared = _x_getBagInt( "&PROP_BAG_NAME;", &PROP_KEY_mTotalBoxesCleared; );

                  mLevelNumber = _x_getBagInt( "&PROP_BAG_NAME;", &PROP_KEY_mLevelNumber; );

                  mCursorCol = _x_getBagInt( "&PROP_BAG_NAME;", &PROP_KEY_mCursorCol; );

                  mCursorRow = _x_getBagInt( "&PROP_BAG_NAME;", &PROP_KEY_mCursorRow; );

 

              <!-- initialize global values based on the game grid size -->

                  mBoxWidth = mScrWidth / mNumCols;

                  mBoxHeight = (mScrHeight - mTextHeight) / mNumRows;

                  mNumBoxes = mNumRows * mNumCols;

 

                  gSeed = _srand( mLevelNumber * 101 );

                  gI = &PROP_KEY_sGameOver; + 1;

                  gCol = 0;

                  gRow = 0;

 

                  sResume = true;

              }

          </script>

      </function>

    </functions>

    <templates>

      ...

    </templates>

    <script>

      ...

      Busy.initialize();

      sPopup = &POPUP_BUSY;;

      sStartup = true;

    </script>

    <display>

      <box>

        <width>

          <eval>mScrWidth</eval>

        </width>

        <height>

          <eval>mScrHeight</eval>

        </height>

        <fg>&_COLOR_BLACK;</fg>

        <bg>&_COLOR_BLACK;</bg>

      </box>

      <fn>

        <text>Back</text>

        <event name="onselect">

          <accelerators>

            <key>F1</key>

          </accelerators>

          <script>

            Busy.show();

            sUnload = true;

          </script>

        </event>

      </fn>

    </display>

    <states>

      <state var="sStartup">

        <transition value="true">

          <delay>20</delay>

          <script>

            if (canResumeGame())

            {

              resumeGame();

            }

            else

            {

              start();

            }

          </script>

        </transition>

      </state>

      ...

      <state var="sResume">

        <transition value="true">

          <delay>10</delay>

          <variables>

            <var name="row" type="int"/>

          </variables>

          <script>

            <!-- for each column -->

 

            mNumBoxesInColumn[ gCol ] = _x_getBagInt( "&PROP_BAG_NAME;", gI );

            gI = gI + 1;

            mColumnHasRemovableBoxes[ gCol ] = _x_getBagBoolean( "&PROP_BAG_NAME;", gI );

            gI = gI + 1;

            mSavedColumnHasRemovableBoxes[ gCol ] = _x_getBagBoolean( "&PROP_BAG_NAME;", gI );

            gI = gI + 1;

 

            row = 0;

 

            while (row &lt; &MAX_ROWS;)

            {

              mBoxes[ row ][ gCol ][ 0 ] = _x_getBagInt( "&PROP_BAG_NAME;", gI );

              gI = gI + 1;

              mBoxes[ row ][ gCol ][ 1 ] = _x_getBagInt( "&PROP_BAG_NAME;", gI );

              gI = gI + 1;

              mSavedBoxes[ row ][ gCol ][ 0 ] = _x_getBagInt( "&PROP_BAG_NAME;", gI );

              gI = gI + 1;

              mSavedBoxes[ row ][ gCol ][ 1 ] = _x_getBagInt( "&PROP_BAG_NAME;", gI );

              gI = gI + 1;

 

              row = row + 1;

            }

   

            repaintColumn( gCol );

 

            <!-- next column -->

            gCol = gCol + 1;

            _clear_state( sResume );

            sResume = gCol &lt; &MAX_COLS;;

          </script>

        </transition>

        <transition value="false">

          <delay>10</delay>

          <script>

            sGameOver = _x_getBagBoolean( "&PROP_BAG_NAME;", &PROP_KEY_sGameOver; );

            _x_closeBag( "&PROP_BAG_NAME;" );

 

            repaint();

 

            if (sGameOver)

            {

              sResume = false;

 

              if (mTotalBoxesCleared == mNumBoxes)

              {

                mMessage = _strcat( "Level cleared!

Bonus: ", _strcat( mScore / 4, "." ) );

              }

              else

              {

                mMessage = _strcat( "No moves.

Penalty: ", _strcat( mNumBoxes - mTotalBoxesCleared, "." ) );

              }

 

              sPopup = &POPUP_MESSAGE;;

            }

            else

            {

              sFnKeys = true;

              sCursor = true;

              sPopup = &POPUP_NULL;;

            }

          </script>

        </transition>

      </state>

      <state var="sUnload">

        <transition value="true">

          <script>

            saveGame();

            _unload();

          </script>

        </transition>

      </state>

    </states>

    <state-machines>

      <include file="ujlib/menu.ujml" state-machine="Menu"/>

      <include file="ujlib/busy.ujml" state-machine="Busy"/>

    </state-machines>

  </application>

</ujml>

            File: ujinn9.ujml

 

Tip: Property bags are not supported on resource-limited platforms that require the minimal Java player (DoJa). So, it is important to use the _isSupported() function to determine if property bags are supported by the platform. If data persistence is required the application can save its data to a server and refetch it when it is resumed.

 

One way to do this is to use two partitions for data saving and fetching: one uses property bags which allow the application to save and fetch the data quickly, and the other partition uses a server. The application then loads the appropriate data fetching partition depending on whether property bags are supported on the device or not.

Localization

An application can determine a device's locale using the _getStringProperty() function.  Below, we move all of Ujinn’s locale-specific data, namely strings, into separate partitions—one for each locale.

 

 

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE ujml PUBLIC "-//UIEVOLUTION//DTD UJML 1.4//EN" "ujml.dtd" [

  <!ENTITY % include SYSTEM "ujlib/include.ujmc">

  %include;

  <!ENTITY % ujinn SYSTEM "ujinn.ujmc">

  %ujinn;

  ...

]>

<ujml>

  <application>

    <state-machines>

      ...

    </state-machines>

 

    <state-variables>

      ...

      <state-var name="sLocaleLoaded" type="boolean" access="export"/>

      <state-var name="sBg" type="boolean"/>

    </state-variables>

    <variables>

      ...

      <!-- Locale strings -->

      <var name="mStrings" type="string" size="&LOCALE_MAX_STRINGS;" access="export"/>

    </variables>

    <functions>

      ...

    </functions>

    <templates>

      ...

    </templates>

    <script>

      _link( "Locale", _strcat( _substring( _getStringProperty( &_PROPERTY_STRING_LOCALE; ), 0, 2 ), "/Locale.ujbc" ) );

      ...

      Busy.initialize();

      sPopup = &POPUP_BUSY;;

    </script>

    <display>

      <box>

        <width>

          <eval>mScrWidth</eval>

        </width>

        <height>

          <eval>mScrHeight</eval>

        </height>

        <fg>&_COLOR_BLACK;</fg>

        <bg>&_COLOR_BLACK;</bg>

      </box>

    </display>

    <states>

      <state var="sBg">

        <transition value="true">

          <display>

            <fn>

              <text>

                <eval>mStrings[ &LOCALE_STR_BACK; ]</eval>

              </text>

              <event name="onselect">

                <accelerators>

                  <key>F1</key>

                </accelerators>

                <script>

                  sUnload = true;

                </script>

              </event>

            </fn>

          </display>

        </transition>

      </state>

      <state var="sLocaleLoaded">

        <transition value="true">

          <script>

            sBg = true;

            sStartup = true;

          </script>

        </transition>

      </state>

      ...

      <state var="sFnKeys">

        <transition value="true">

          <display>

            <fn>

              <text>

                <eval>mStrings[ &LOCALE_STR_OPTIONS; ]</eval>

              </text>

              ...

            </fn>

          </display>

        </transition>

      </state>

      ...

      <state var="sPopup">

        ...

        <transition value="&POPUP_MAIN_MENU;">

          <script>

            Menu.initialize( "menu" );

            Menu.add( mStrings[ &LOCALE_STR_MENU_NEW; ], &ON_NEXT_LEVEL; );

            Menu.add( mStrings[ &LOCALE_STR_MENU_RESTART; ], &ON_RESTART; );

            Menu.add( mStrings[ &LOCALE_STR_MENU_DONE; ], &ON_NULL; );

            Menu.center();

            Menu.setVisible( true );

          </script>

        </transition>

        <transition value="&POPUP_MESSAGE;">

          <display>

            <box>

              <x>

                <eval>2</eval>

              </x>

              <y>

                <eval>2</eval>

              </y>

              <width>

                <eval>mScrWidth - 4</eval>

              </width>

              <height>

                <eval>mScrHeight - 4</eval>

              </height>

              <bg>&_COLOR_DARKGRAY;</bg>

              <multi-label>

                <text>

                  <ref var="mMessage"/>

                </text>

                <x>2</x>

                <y>2</y>

                <width>

                  <eval>mScrWidth - 8</eval>

                </width>

                <height>

                  <eval>_max( mScrHeight - (mTextHeight * 2) - 4, mTextHeight * 2 )</eval>

                </height>

                <fg>&_COLOR_WHITE;</fg>

                <bg>&_COLOR_DARKGRAY;</bg>

              </multi-label>

            </box>

          </display>

          <script>

            Menu.initialize( "menu" );

            Menu.add( mStrings[ &LOCALE_STR_MENU_RESTART; ], &ON_RESTART; );

            Menu.add( mStrings[ &LOCALE_STR_MENU_NEW; ], &ON_NEXT_LEVEL; );

            Menu.center();

            Menu.setVisible( true );

          </script>

        </transition>

      </state>

      ...

      <state var="sDelayedEndGameCheck">

        <transition value="true">

          <delay>100</delay>

          <variables>

            <var name="fGameOver" type="boolean"/>

          </variables>

          <script>

            <!-- see if the end of the game has been reached. Either all boxes have been removed -->

            <!-- or it is not possible to remove any more boxes. -->

 

            if (mTotalBoxesCleared == mNumBoxes)

            {

            <!-- we have a winner! all boxes have been removed. -->

              mScore = mScore + (mScore / 4);

              mMessage = _strcat( mStrings[ &LOCALE_STR_LEVEL_CLEARED; ], mScore / 4 );

              sPopup = &POPUP_MESSAGE;;

 

              if (mScore &gt; mHighScore)

              {

                mHighScore = mScore;

              }

 

              fGameOver = true;

            }

            else

            {

            <!-- determine if there are removable boxes -->

 

              if (!areBoxesRemovable())

              {

            <!-- there are no more selectable boxes so the game is over -->

                mScore = mScore - (mNumBoxes - mTotalBoxesCleared);

                mMessage = _strcat( mStrings[ &LOCALE_STR_NO_MOVES_PENALTY; ], mNumBoxes - mTotalBoxesCleared );

                sPopup = &POPUP_MESSAGE;;

 

                if (mScore &gt; mHighScore)

                {

                  mHighScore = mScore;

                }

 

                fGameOver = true;

              }

            }

 

            updateScores();

            <!-- repaint cursor -->

            sCursor = false;

            sCursor = true;

            sDelayedEndGameCheck = false;

            sGameOver = fGameOver;

          </script>

        </transition>

      </state>

      ...

      <state var="sResume">

        ...

        <transition value="false">

          <delay>10</delay>

          <script>

            sGameOver = _x_getBagBoolean( "&PROP_BAG_NAME;", &PROP_KEY_sGameOver; );

            _x_closeBag( "&PROP_BAG_NAME;" );

 

            repaint();

 

            if (sGameOver)

            {

              sResume = false;

 

              if (mTotalBoxesCleared == mNumBoxes)

              {

                mMessage = _strcat( mStrings[ &LOCALE_STR_LEVEL_CLEARED; ], mScore / 4 );

              }

              else

              {

                mMessage = _strcat( mStrings[ &LOCALE_STR_NO_MOVES_PENALTY; ], mNumBoxes - mTotalBoxesCleared );

              }

 

              sPopup = &POPUP_MESSAGE;;

            }

            else

            {

              sFnKeys = true;

              sCursor = true;

              sPopup = &POPUP_NULL;;

            }

          </script>

        </transition>

      </state>

      <state var="sUnload">

        <transition value="true">

          <script>

            saveGame();

            _unload();

          </script>

        </transition>

      </state>

    </states>

    <state-machines>

      <include file="ujlib/menu.ujml" state-machine="Menu"/>

      <include file="ujlib/busy.ujml" state-machine="Busy"/>

    </state-machines>

  </application>

</ujml>

            File: ujinn10.ujml

 

Below is the external entity file that defines a constant for each string stored in mStrings.   This file is shared by Ujinn and the locale partitions.

 

<?xml version="1.0" encoding="UTF-8"?>

<!-- Locale -->

<!ENTITY LOCALE_STR_MENU_NEW "0">

<!ENTITY LOCALE_STR_MENU_RESTART "1">

<!ENTITY LOCALE_STR_MENU_SETTINGS "2">

<!ENTITY LOCALE_STR_MENU_DONE "3">

<!ENTITY LOCALE_STR_OPTIONS_LEVEL "4">

<!ENTITY LOCALE_STR_OPTIONS_COLS "5">

<!ENTITY LOCALE_STR_OPTIONS_ROWS "6">

<!ENTITY LOCALE_STR_OPTIONS_COLORS "7">

<!ENTITY LOCALE_STR_BACK "8">

<!ENTITY LOCALE_STR_OPTIONS "9">

<!ENTITY LOCALE_STR_NO_MOVES "10">

<!ENTITY LOCALE_STR_LEVEL_CLEARED "11">

<!ENTITY LOCALE_STR_NO_MOVES_PENALTY "12">

<!ENTITY LOCALE_MAX_STRINGS "13">

            File: ujinn.ujmc

 

Below are the English and Japanese locale partitions.

 

<?xml version="1.0" encoding="UTF-16"?>

<!DOCTYPE ujml PUBLIC "-//UIEVOLUTION//DTD UJML 1.2//EN" "../ujml.dtd" [

    <!ENTITY % ujinn SYSTEM "../ujinn.ujmc">

    %ujinn;

]>

<ujml>

    <application>

       <state-variables>

          <state-var name="sLocaleLoaded" type="boolean" access="import"/>

       </state-variables>

       <variables>

         <var name="mStrings" type="string" size="&LOCALE_MAX_STRINGS;" access="import"/>

       </variables>

       <script>

          mStrings[ &LOCALE_STR_MENU_NEW; ] = "Next Level";

          mStrings[ &LOCALE_STR_MENU_RESTART; ] = "Replay Level";

          mStrings[ &LOCALE_STR_MENU_SETTINGS; ] = "Settings";

          mStrings[ &LOCALE_STR_MENU_DONE; ] = "Done";

          mStrings[ &LOCALE_STR_OPTIONS_LEVEL; ] = "Level";

          mStrings[ &LOCALE_STR_OPTIONS_COLS; ] = "Columns";

          mStrings[ &LOCALE_STR_OPTIONS_ROWS; ] = "Rows";

          mStrings[ &LOCALE_STR_OPTIONS_COLORS; ] = "Colors";

          mStrings[ &LOCALE_STR_BACK; ] = "Back";

          mStrings[ &LOCALE_STR_OPTIONS; ] = "Options";

          mStrings[ &LOCALE_STR_NO_MOVES; ] = "No moves.";

          mStrings[ &LOCALE_STR_LEVEL_CLEARED; ] = "Level cleared! Bonus: ";

          mStrings[ &LOCALE_STR_NO_MOVES_PENALTY; ] = "No moves. Penalty: ";

 

          sLocaleLoaded = true;

       </script>

    </application>

</ujml>

            File: en/Locale.ujml

 

<!DOCTYPE ujml PUBLIC "-//UIEVOLUTION//DTD UJML 1.2//EN" "../ujml.dtd" [

    <!ENTITY % ujinn SYSTEM "../ujinn.ujmc">

    %ujinn;

]>

<ujml>

    <application>

        <state-variables>

            <state-var name="sLocaleLoaded" type="boolean" access="import"/>

        </state-variables>

        <variables>

            <var name="mStrings" type="string" size="&LOCALE_MAX_STRINGS;"

                 access="import"/>

        </variables>

        <script>

            mStrings[ &LOCALE_STR_MENU_NEW; ] = "次のレベル";

            mStrings[ &LOCALE_STR_MENU_RESTART; ] = "リプレイ";

            mStrings[ &LOCALE_STR_MENU_SETTINGS; ] = "設定";

            mStrings[ &LOCALE_STR_MENU_DONE; ] = "済み";

            mStrings[ &LOCALE_STR_OPTIONS_LEVEL; ] = "レベル";

            mStrings[ &LOCALE_STR_OPTIONS_COLS; ] = "";

            mStrings[ &LOCALE_STR_OPTIONS_ROWS; ] = "";

            mStrings[ &LOCALE_STR_OPTIONS_COLORS; ] = "";

            mStrings[ &LOCALE_STR_BACK; ] = "戻る";

            mStrings[ &LOCALE_STR_OPTIONS; ] = "オプション";

            mStrings[ &LOCALE_STR_NO_MOVES; ] = "手詰まり.";

            mStrings[ &LOCALE_STR_LEVEL_CLEARED; ] = "レベルクリア!ボーナス: ";

            mStrings[ &LOCALE_STR_NO_MOVES_PENALTY; ] = "手詰まり. 減点: ";

 

            sLocaleLoaded = true;

        </script>

    </application>

</ujml>

            File: ja/Locale.ujml

 

Using the SDK Debugger, you can select a locale to test the application in both English and Japanese. Use the Tools ŕ Select menu to display the appropriate dialog box to change the locale:

 

 

 

Animation

Effective use of animation is important to achieve a rich user experience.

 

There are two ways to implement animation in UJML: the fixed-timer method and the maximum-frame-rate method. The fixed-timer method is appropriate when a sequence of objects needs to be displayed in a particular order with particular intervals; the maximum-frame-rate method is appropriate when a set of objects needs to be moved as smoothly as possible.

 

The first example displays a character walking from right to left across the screen using fixed-timer animation. It has two state variables, sBoy and sTick. The integer state variable sBoy has two <display> blocks, one for the value 0, and another for the value 1 which display  and  respectively at the position specified by the variable mPos. The boolean state variable sTick has a <delay> element which executes the <script> element 250 milliseconds after sTick turns to true. The <script> block calls the function processTick(), which moves the position of the character by changing the variable mPos, alternates the state variable sBoy, and resets the state variable sTick to true until the character reaches the left edge of the screen. As a result, the function processTick() will be called every 250 milliseconds until the character reaches the left edge and animates the character from right to left.

 

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE ujml PUBLIC "-//UIEVOLUTION//DTD UJML 1.2//EN" "../ujml.dtd" [

       <!ENTITY MSEC_FRAME "250">

       <!ENTITY BOYW "20">

       <!ENTITY BOYH "24">

       <!ENTITY SBOY_L0 "0">

       <!ENTITY SBOY_L1 "1">

       <!ENTITY IMAGE_URL "<image-url>boy</image-url>">

]>

<ujml>

       <application>

              <state-variables>

                     <state-var name="sTick" type="boolean" />

                     <state-var name="sBoy" type="int" />

              </state-variables>

              <variables>

                     <var name="mScrWidth" type="int" />

                     <var name="mScrHeight" type="int" />

                     <var name="mPosX" type="int" />

              </variables>

              <functions>

                     <function name="processTick" type="void">

                           <script>

                                  if (mPosX>-&BOYW;) {

                                         mPosX = mPosX-&BOYW;/4;

                                         sBoy = (1-sBoy);

                                         sTick = false;

                                         sTick = true;

                                  }     

                           </script>

                     </function>

              </functions>

              <script>

                     mScrWidth = _getIntProperty(&_PROPERTY_INT_SCREEN_WIDTH;);

                     mScrHeight = _getIntProperty(&_PROPERTY_INT_SCREEN_HEIGHT;);

                     mPosX = mScrWidth;

                     sBoy = &SBOY_L0; ;

                     sTick = true;

              </script>

              <states>

                     <state var="sTick">

                           <transition value="true">

                                   <delay>&MSEC_FRAME;</delay>

                                  <script>processTick();</script>

                           </transition>

                     </state>

                     <state var="sBoy">

                           <transition value="&SBOY_L0;">

                                  <display>

                                         <image>

                                                <url>&IMAGE_URL;</url>

                                                <x><eval>mPosX</eval></x>

                                                <y><eval>mScrHeight/2</eval></y>

                                                <width>&BOYW;</width>

                                                <height>&BOYH;</height>

                                         </image>

                                  </display>

                           </transition>

                           <transition value="&SBOY_L1;">

                                  <display>

                                         <image>

                                                <url>&IMAGE_URL;</url>

                                                <src-y>&BOYH;</src-y>

                                                <x><eval>mPosX</eval></x>

                                                <y><eval>mScrHeight/2</eval></y>

                                                <width>&BOYW;</width>

                                                <height>&BOYH;</height>

                                         </image>

                                  </display>

                           </transition>

                     </state>

              </states>

       </application>

</ujml>

File: walk.ujml

 

Here is the result:

 

The second example turns the first example above into an interactive application, which allows the user to control the character's movement.

 

In addition to sTick and sBoy, this application has the third state variable sInput, which enables user input. The state variable sInput has a single <display> element for the state value true, which has four dummy <box> elements, which processes direction keys—Up, Down, Left, and Right keys. When one of those direction keys is pressed, the function moveBoy() is called with an appropriate set of parameters. The function moveBoy() changes the state variable sBoy so that the image faces the correct direction, stores the direction in mDX and mDY, then resets the state variable sTick to true to start the animation.

 

Note that it changes the state variable sInput to false in moveBoy() and keeps it false until the end of the animation cycle. This is important to prevent it from reentering an animation in the middle of animation, which is a very common mistake made by novice programmers.

 

The state variable sBoy has eight <display> elements instead of two, to display two alternative images for each direction.

The state variable sTick is identical to the previous example, but the function processTick() is slightly different to support four different directions, and stops after it displays two alternative images.

 

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE ujml PUBLIC "-//UIEVOLUTION//DTD UJML 1.2//EN" "../ujml.dtd" [

       <!ENTITY MSEC_FRAME "100">

       <!ENTITY BOYW "20">

       <!ENTITY BOYH "24">

       <!ENTITY SBOY_L0 "0">

       <!ENTITY SBOY_L1 "1">

       <!ENTITY SBOY_R0 "2">

       <!ENTITY SBOY_R1 "3">

       <!ENTITY SBOY_U0 "4">

       <!ENTITY SBOY_U1 "5">

       <!ENTITY SBOY_D0 "6">

       <!ENTITY SBOY_D1 "7">

       <!ENTITY IMAGE_URL "<image-url>boy</image-url>">

]>

<ujml>

       <application>

              <state-variables>

                     <state-var name="sTick" type="boolean"/>

                     <state-var name="sBoy" type="int"/>

                     <state-var name="sInput" type="boolean"/>

              </state-variables>

              <variables>

                     <var name="mScrWidth" type="int"/>

                     <var name="mScrHeight" type="int"/>

                     <var name="mPosX" type="int"/>

                     <var name="mPosY" type="int"/>

                     <var name="mDX" type="int"/>

                     <var name="mDY" type="int"/>

              </variables>

              <functions>

                     <function name="processTick" type="void">

                           <script>

                                  mPosX = mPosX + mDX;

                                  mPosY = mPosY + mDY;

                                  sBoy = (sBoy /2 *2) + 1 - (sBoy % 2);

                                  if (_eq(sBoy % 2,1)) {

                                         sTick = false;

                                         sTick = true;

                                  } else {

                                         sInput = true;

                                  }     

                           </script>

                     </function>

                     <function name="moveBoy" type="void">

                           <parameters>

                                  <var name="dx" type="int"/>

                                  <var name="dy" type="int"/>

                                  <var name="state" type="int"/>

                                  <var name="fMove" type="boolean"/>

                           </parameters>

                           <script>

                                  sBoy = state;

                                  if (fMove) {

                                         mDX = (dx * &BOYW;)/2;

                                         mDY = (dy * &BOYH;)/2;

                                         sInput = false;

                                         sTick = false;

                                         sTick = true;

                                  }

                           </script>

                     </function>

              </functions>

              <script>

                     mScrWidth = _getIntProperty(&_PROPERTY_INT_SCREEN_WIDTH;);

                     mScrHeight = _getIntProperty(&_PROPERTY_INT_SCREEN_HEIGHT;);

                     mPosX = (mScrWidth-&BOYW;)/2;

                     mPosY = (mScrHeight-&BOYH;)/2;

                     sBoy = &SBOY_L0; ;

                     sInput = true;

              </script>

              <states>

                     <state var="sTick">

                           <transition value="true">

                                  <delay>&MSEC_FRAME;</delay>

                                  <script>processTick();</script>

                           </transition>

                     </state>

                     <state var="sInput">

                           <transition value="true">

                                  <display>

                                         <box>

                                                <event name="onselect">

                                                       <accelerators>

                                                              <key>LEFT</key>

                                                       </accelerators>

                                                       <script>

                                                              moveBoy(-1, 0, &SBOY_L0;, _gt(mPosX-&BOYW;, -1));

                                                       </script>

                                                </event>

                                         </box>

                                         <box>

                                                <event name="onselect">

                                                       <accelerators>

                                                              <key>RIGHT</key>

                                                       </accelerators>

                                                       <script>

                                                              moveBoy(1, 0, &SBOY_R0;, _lt(mPosX+&BOYW;*2, mScrWidth));

                                                       </script>

                                                </event>

                                         </box>

                                         <box>

                                                <event name="onselect">

                                                       <accelerators>

                                                              <key>UP</key>

                                                       </accelerators>

                                                       <script>

                                                              moveBoy(0, -1, &SBOY_U0;, _gt(mPosY-&BOYH;, -1));

                                                       </script>

                                                </event>

                                         </box>

                                         <box>

                                                <event name="onselect">

                                                       <accelerators>

                                                              <key>DOWN</key>

                                                       </accelerators>

                                                       <script>

                                                              moveBoy(0, 1, &SBOY_D0;, _lt(mPosY+&BOYH;*2, mScrHeight));

                                                       </script>

                                                </event>

                                         </box>

                                  </display>

                           </transition>

                     </state>

                     <state var="sBoy">

                           <transition value="&SBOY_L0;">

                                  <display>

                                         <image>

                                                <url>&IMAGE_URL;</url>

                                                <x><eval>mPosX</eval></x>

                                                <y><eval>mPosY</eval></y>

                                                <width>&BOYW;</width>

                                                <height>&BOYH;</height>

                                         </image>

                                  </display>

                           </transition>

                           <transition value="&SBOY_L1;">

                                  <display>

                                         <image>

                                                <url>&IMAGE_URL;</url>

                                                <src-y>&BOYH;</src-y>

                                                <x><eval>mPosX</eval></x>

                                                <y><eval>mPosY</eval></y>

                                                <width>&BOYW;</width>

                                                <height>&BOYH;</height>

                                         </image>

                                  </display>

                           </transition>

                           <transition value="&SBOY_R0;">

                                  <display>

                                         <image>

                                                <url>&IMAGE_URL;</url>

                                                <src-x>&BOYW;</src-x>

                                                <x><eval>mPosX</eval></x>

                                                <y><eval>mPosY</eval></y>

                                                <width>&BOYW;</width>

                                                <height>&BOYH;</height>

                                         </image>

                                  </display>

                           </transition>

                           <transition value="&SBOY_R1;">

                                  <display>

                                         <image>

                                                <url>&IMAGE_URL;</url>

                                                <src-x>&BOYW;</src-x>

                                                <src-y>&BOYH;</src-y>

                                                <x><eval>mPosX</eval></x>

                                                <y><eval>mPosY</eval></y>

                                                <width>&BOYW;</width>

                                                <height>&BOYH;</height>

                                         </image>

                                  </display>

                           </transition>

                           <transition value="&SBOY_U0;">

                                  <display>

                                         <image>

                                                <url>&IMAGE_URL;</url>

                                                <src-x><eval>&BOYW;*2</eval></src-x>

                                                <x><eval>mPosX</eval></x>

                                                <y><eval>mPosY</eval></y>

                                                <width>&BOYW;</width>

                                                <height>&BOYH;</height>

                                         </image>

                                  </display>

                           </transition>

                           <transition value="&SBOY_U1;">

                                  <display>

                                         <image>

                                                <url>&IMAGE_URL;</url>

                                                <src-x><eval>&BOYW;*2</eval></src-x>

                                                <src-y>&BOYH;</src-y>

                                                <x><eval>mPosX</eval></x>

                                                <y><eval>mPosY</eval></y>

                                                <width>&BOYW;</width>

                                                <height>&BOYH;</height>

                                         </image>

                                  </display>

                           </transition>

                           <transition value="&SBOY_D0;">

                                  <display>

                                         <image>

                                                <url>&IMAGE_URL;</url>

                                                <src-x><eval>&BOYW;*3</eval></src-x>

                                                <x><eval>mPosX</eval></x>

                                                <y><eval>mPosY</eval></y>

                                                <width>&BOYW;</width>

                                                <height>&BOYH;</height>

                                         </image>

                                  </display>

                           </transition>

                           <transition value="&SBOY_D1;">

                                  <display>

                                         <image>

                                                <url>&IMAGE_URL;</url>

                                                <src-x><eval>&BOYW;*3</eval></src-x>

                                                <src-y>&BOYH;</src-y>

                                                <x><eval>mPosX</eval></x>

                                                <y><eval>mPosY</eval></y>

                                                <width>&BOYW;</width>

                                                <height>&BOYH;</height>

                                         </image>

                                  </display>

                           </transition>

                     </state>

              </states>

       </application>

</ujml>

File: walk2.ujml

 

The result looks like this:

 

The third example demonstrates how to smoothly animate an object (such as an image or text) from one location to another using the maximum-frame-rate animation technique. In this case, we do not use a fixed <delay> timer like the above examples because it is difficult to pick a single frame-rate that generates reasonable results on all devices. If the device cannot handle all the instructions within the specified amount of time, the animation becomes very slow and jerky. On the other hand, choosing a large delay to make it possible to run it on low-end devices degrades animation quality on high-end devices.   

 

In this situation, the maximum-frame-rate method is appropriate. It simply puts a minimum delay (such as 20 milliseconds) between frames, and adjusts the location of the object based on the actual “elapsed” time. Animations implemented with this method automatically move at the maximum possible frame-rate on each target device.

 

The function processTick() in this example calculates the elapsed time, mElapsedTime, and determines the location of the object, mPos, based on it. For each frame, the state variable sImage will be reset to true, displaying an image at the position specified by the variable mPos.

 

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE ujml PUBLIC "-//UIEVOLUTION//DTD UJML 1.2//EN" "../ujml.dtd" [

       <!ENTITY MSEC_DELAY "20">

       <!ENTITY MSEC_TOTAL "2000">

       <!ENTITY IMAGEW "118">

       <!ENTITY IMAGEH "100">

]>

<ujml>

       <application>

              <state-variables>

                     <state-var name="sTick" type="boolean" />

                     <state-var name="sImage" type="boolean" />

              </state-variables>

              <variables>

                     <var name="mScrWidth" type="int" />

                     <var name="mScrHeight" type="int" />

                     <var name="mOffsetX" type="int" />

                     <var name="mOffsetY" type="int" />

                     <var name="mPos" type="int" />

                     <var name="mStartTime" type="int" />

              </variables>

              <functions>

                     <function name="processTick" type="void">

                           <variables>

                                  <var name="mElapsedTime" type="int" />

                           </variables>

                           <script>

                                  sTick = false;

                                  mElapsedTime = _msec()-mStartTime;

                                  if (_lt(mElapsedTime, &MSEC_TOTAL;)) {

                                         mPos = mScrHeight-mElapsedTime*(mScrHeight-mOffsetY)/&MSEC_TOTAL;;

                                         sTick = true;

                                  } else {

                                         mPos = mOffsetY;

                                  }

                                  sImage = false;

                                  sImage = true;

                           </script>

                     </function>

              </functions>

              <script>

                     mScrWidth = _getIntProperty(&_PROPERTY_INT_SCREEN_WIDTH;);

                     mScrHeight = _getIntProperty(&_PROPERTY_INT_SCREEN_HEIGHT;);

                     mOffsetX = (mScrWidth-&IMAGEW;)/2;

                     mOffsetY = (mScrHeight-&IMAGEH;)/2;

                     mStartTime = _msec();

                     sTick = true;

              </script>

              <states>

                     <state var="sTick">

                           <transition value="true">

                                  <delay>&MSEC_DELAY;</delay>

                                  <script>processTick();</script>

                           </transition>

                     </state>

                     <state var="sImage">

                           <transition value="true">

                                  <display>

                                         <image>

                                                <url><image-url>splash</image-url></url>

                                                <x><eval>mOffsetX</eval></x>

                                                <y><eval>mPos</eval></y>

                                                <width>&IMAGEW;</width>

                                                <height>&IMAGEH;</height>

                                         </image>

                                  </display>

                           </transition>

                     </state>

              </states>

       </application>

</ujml>

File: splash.ujml

 

Here is the result:

 

Animating more than one object using this method is also possible. See the example below. In this case, each display block of the state variable array sImage[] will be redrawn for each processTick() call.

 

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE ujml PUBLIC "-//UIEVOLUTION//DTD UJML 1.2//EN" "../ujml.dtd" [

       <!ENTITY MSEC_DELAY "20">

       <!ENTITY MSEC_TOTAL "2000">

       <!ENTITY IMAGEW "118">

       <!ENTITY IMAGEH "100">

       <!ENTITY IMAGEC "10">

       <!ENTITY SLICEH "10">

]>

<ujml>

       <application>

              <state-variables>

                     <state-var name="sTick" type="boolean" />

                     <state-var name="sImages" type="boolean" size="&IMAGEC;" />

              </state-variables>

              <variables>

                     <var name="mScrWidth" type="int" />

                     <var name="mScrHeight" type="int" />

                     <var name="mOffsetX" type="int" />

                     <var name="mOffsetY" type="int" />

                     <var name="mStartTime" type="int" />

                     <var name="mPos" type="int" />

              </variables>

              <functions>

                     <function name="processTick" type="void">

                           <variables>

                                  <var name="i" type="int" />

                                  <var name="mElapsedTime" type="int" />

                           </variables>

                           <script>

                                  sTick = false;

                                  mElapsedTime = _msec()-mStartTime;

                                  if (_lt(mElapsedTime, &MSEC_TOTAL;)) {

                                         mPos = mElapsedTime*(mScrWidth-mOffsetX)/&MSEC_TOTAL;;

                                         sTick = true;

                                  } else {

                                         mPos = mScrWidth-mOffsetX;

                                  }

                                  i=0;

                                  while(_lt(i,&IMAGEC;)) {

                                         sImages[i] = false;

                                         sImages[i] = true;

                                         i = i+1;

                                  }     

                           </script>

                     </function>

              </functions>

              <script>

                     mScrWidth = _getIntProperty(&_PROPERTY_INT_SCREEN_WIDTH;);

                     mScrHeight = _getIntProperty(&_PROPERTY_INT_SCREEN_HEIGHT;);

                     mOffsetX = (mScrWidth-&IMAGEW;)/2;

                     mOffsetY = (mScrHeight-&IMAGEH;)/2;

                     mStartTime = _msec();

                     sTick = true;

              </script>

              <states>

                     <state var="sTick">

                           <transition value="true">

                                  <delay>&MSEC_DELAY;</delay>

                                  <script>processTick();</script>

                           </transition>

                     </state>

                     <state var="sImages" index="*">

                           <transition value="true">

                                  <display>

                                         <image>

                                                <url><image-url>splash</image-url></url>

                                                <src-y><eval>_state_index(0)*&SLICEH;</eval></src-y>

                                                <x><eval>mOffsetX+(mScrWidth-mOffsetX-mPos)*((_state_index(0)%2)*2-1)</eval></x>

                                                <y><eval>mOffsetY+_state_index(0)*&SLICEH;</eval></y>

                                                <width>&IMAGEW;</width>

                                                <height>&SLICEH;</height>

                                         </image>

                                  </display>

                           </transition>

                     </state>

              </states>

       </application>

</ujml>

File: splash2.ujml

 

Reusable Components

A UJML program isn’t really a single state machine; it is an environment where multiple dependent state machines can function and interact. In many cases, the program has several sets of related state variables, variables, functions, and templates where each set implements a higher level state machine, for example, a button. The <state-machine> element defines an encapsulation of these related elements so that it is easier to develop and reuse them as a unit.

 

A <state-machine> is more like a service API than an object-oriented class. A <state-machine> cannot be instantiated like a class. Once it is included (using <include> or linked (using _link()) into an application, it becomes an encapsulated service API to the application. While it is possible to include or link a <state-machine> multiple times in an application, simultaneously, each instance is a complete copy of all the byte code and memory needed by the UIE Player to execute the <state-machine>. Unlike an object-oriented class where each instance shares the code that implements the class initializers and member functions, state machines do not share such resources; they are more like a macro expansion or a linked library.

 

This does not preclude a <state-machine> from sharing member functions by managing its own notion of an instance. As with other non-object-oriented APIs, a <state-machine> can expose the notion of a handle which it uses internally to keep track of an instance. With this model, each member function takes at least one parameter for the instance handle.

 

A RecordSet service might have member functions that look like this:

 

·        handle ß RecordSet.Open(name)

·        RecordSet.Seek(handle, offset)

·        RecordSet.Read(handle)

·        RecordSet.Close(handle)

 

<state-machine>

This element defines a UJML state machine.

 

Syntax:

<state-machine name=”{string}” [access=”import | export”]/>

            [<state-machines>…</state-machines>]

            [<state-variables>…</state-variables>]

            [<variables>…</variables>]

            [<functions>…</functions>]

            [<templates>…</templates>]

            [<states>]…[</states>]

            [<state-machines>…</state-machines>]

</state-machine>

 

All variables, functions, and templates are, by default, private to the state-machine. This allows a state machine to be fully encapsulated. However, they can be declared public using the attribute visibility="public". The attribute values public and private are new attribute values that are valid only in the context of a <state-machine>.

If access=export is specified for the state machine, then all public members are exported. If access=import is specified, then only public members may be declared in the state machine and a parent or ancestor partition must export the state machine.

The only way that an application can access or directly affect the state of a state machine is by calling the state machine's public members. The syntax for calling state machine functions is:

 

{state-machine-name}.{function-name}([function-arguments]])  

 

or

 

{state-machine-name}.{member-name} = value

 

Examples: 

slides.init();

slides.show( 0 );

grid.repaint()

grid.mVariable = 33;

 

If a state machine needs to be initialized, it must define a public function that can be called by the application to initialize it. For example, myStateMachine.init().

 A state-machine's z-order is defined by its location in the <application> or <paritition>; bottom to top. 

 Examples:

 “Slide Viewer” Example: 

<ujml>

  <application>

    <state-machines>

 

       <state-machine name=”slides”/>

 

        <state-variables>

           <state-var name=”self” type=”int”/>

        </state-variables>

        <variables>

          <var name=”field1” type=”string”/>

          <var name=”field2” type=”int”/>

        </variables>

 

        <functions>

          <function name=”showPage” type=”void”

                     visiblity =”public”>

            <parameters>

              <var name=”pg” type=”int”/>

              <var name=”fld1” type=”string”/>

              <var name=”fld2” type=”int”/>

            </parameters>

            <script>

              field1 = fld1;

              field2 = fld2;

              self = pg;

            </script>

          </function>

        </functions>

 

        <states>

         <state var=”self”>

          <transition value=”0”>

            <display>

               …

            </display>

            <script>

               …  

            </script>

          </transition>

          <transition value=”1”>

            <display>

                …

            </display>

            <script>

                …  

             </script>

          </transition>

         </state>

       </states>

      </state-machine>

 

Ignoring the private/public functions feature, the equivalent of the above state machine written in “standard” UJML is as follows:

 

<ujml>

  <application>

    <state-variables>

       <state-var name=”slides” type=”int”/>

    </state-variables>

    <variables>

      <var name=”field1” type=”string”/>

      <var name=”field2” type=”int”/>

    </variables>

 

    <functions>

      <function name=”showPage” type=”void” visiblity =”public”>

        <parameters>

          <var name=”pg” type=”int”/>

          <var name=”fld1” type=”string”/>

          <var name=”fld2” type=”int”/>

        </parameters>

        <script>

          field1 = fld1;

          field2 = fld2;

          slides = pg;

        </script>

      </function>

    </functions>

 

    <states>

      <state var=”slides”>

          <transition value=”0”>

            <display>…</display>

            <script> …  </script>

          </transition>

          <transition value=”1”>

            <display>…</display>

            <script> …  </script>

          </transition>

        </state>

      </ states>

 

Grid example:

   <state-machine name=”grid”>

     <state-variables>

       <state-var name=”self” type=”int” size=”10,10”>

      </state-variables>

      <variables>…</variables>

      <functions>

        <function name=”show” type=”void” visiblity=”public”>

          <parameters>

            <var name=”x” type=”int”/>

            <var name=”y” type=”int”/>

            …

          </parameters>

          <script>

            …

            self[ x ][ y ] = 0;

          </script>

         </function>

        </functions>

        <states>

         <state var=”self” index=”*,*”>

          <transition value=”0”>

            <display>…</display>

            …

            <script>

              …

            </script>

 

To use either one of the above:

slides.showPage( 0, “fred flintstone”, 31 );

grid.show( 0, 10, … );

 

<include>

Allows a state machine defined in one UJML file to be included in another UJML file, for example, including file A within file B.. The contents of the state machine definition text from file A replace the <include> tag in the file that contains the <include>.

 

<include file=”filename” state-machine=”{string}”/>

Examples:

File (slides.ujml) 

<ujml>

  <partition>

    <state-machines>

      <state-machine name=”slides” >

     …

      </state-machine>

    </state-machines>

      …

  </partition>

</ujml>

 

File including above file: 

<ujml>

  <partition>

    <state-machines>

      <include “slides.ujml” state-machine=”slides”/>

    </state-machines>

    …

      slides.showPage( 0, “fred flintstone”, 31 );

      …

  </partition>

</ujml>

 

The above example includes the slides state machine into the application; the example below imports it instead, which allows the slides state machine to be in its own partition and available for reuse by other partitions.

File slidesinc.ujml: 

<ujml>

  <partition>

    <state-machines>

 

      <state-machine name=”slides” access=”import”/>

        <functions>

          <functions name=”showPage” type=”void”

                     visibility=”public”>

            <parameters>

              <var name=”pg” type=”int”/>

              <var name=”fld1” type=”string”/>

              <var name=”fld2” type=”int”/>

            </parameters>

          </function>

        </functions>

      </state-machine>

    </state-machines>

  < partition >

</ujml>

 

 

The application file: 

<ujml>

  <partition>

    <state-machines>

      <include “slidesinc.ujml” state-machine=”slides”/>

    </state-machines>

      …

       slides.showPage( 0, “fred flintstone”, 31 );

      …

  </partition>

</ujml>

 

It is assumed that a parent partition linked “slides.ujbc”, which then linked the above partition.

Examples

Most of the following examples can be found in the ujml/ujlib folder.

 

If you want to link them (using the _link() function) into your applications, use ujml/ujlib/include.ujml in your <include> elements; <include file=”ujlib/include.ujml” state-machine=”Busy”/>.   

 

If you want to include them directly, use each example’s source file; for example, to include the Busy state machine: <include file=”ujlib/busy.ujml” state-machine=”Busy”/>.

 

Example usage for these state machines can be found in the applications in ujml/samples folder.

Busy

      <state-machine name="Busy" access="import">

        <functions>

          <function name="initialize" type="void" visibility="public"/>

          <function name="show" type="void" visibility="public"/>

          <function name="hide" type="void" visibility="public"/>

        </functions>

      </state-machine>

Button

      <state-machine name="Button" access="import">

        <functions>

          <function name="create" type="int" visibility="public"/>

 

          <function name="destroy" type="void" visibility="public">

            <parameters>

              <var name="handle" type="int"/>

            </parameters>

          </function>

 

          <function name="bind" type="void" visibility="public">

            <parameters>

              <var name="handle" type="int"/>

              <var name="handler" type="string"/>

            </parameters>

          </function>

 

          <function name="setVisible" type="void" visibility="public">

            <parameters>

              <var name="handle" type="int"/>

              <var name="visible" type="boolean"/>

            </parameters>

          </function>

 

          <function name="setPosition" type="void" visibility="public">

            <parameters>

              <var name="handle" type="int"/>

              <var name="x" type="int"/>

              <var name="y" type="int"/>

            </parameters>

          </function>

 

          <function name="setSize" type="void" visibility="public">

            <parameters>

              <var name="handle" type="int"/>

              <var name="w" type="int"/>

              <var name="h" type="int"/>

            </parameters>

          </function>

 

          <function name="setColor" type="void" visibility="public">

            <parameters>

              <var name="handle" type="int"/>

              <var name="index" type="int"/>

              <var name="color" type="int"/>

            </parameters>

          </function>

 

          <function name="setText" type="void" visibility="public">

            <parameters>

              <var name="handle" type="int"/>

              <var name="text" type="string"/>

            </parameters>

          </function>

 

          <function name="setOrder" type="void" visibility="public">

            <parameters>

              <var name="handle" type="int"/>

              <var name="order" type="int"/>

            </parameters>

          </function>

 

          <function name="setFocus" type="void" visibility="public">

            <parameters>

              <var name="handle" type="int"/>

              <var name="focus" type="boolean"/>

            </parameters>

          </function>

        </functions>

      </state-machine>

 

Edit

      <state-machine name="Edit" access="import">

        <functions>

          <function name="destroy" type="void" visibility="public">

          </function>

 

          <function name="bind" type="void" visibility="public">

            <parameters>

              <var name="handler" type="string"/>

            </parameters>

          </function>

 

          <function name="setVisible" type="void" visibility="public">

            <parameters>

              <var name="visible" type="boolean"/>

            </parameters>

          </function>

 

          <function name="setPosition" type="void" visibility="public">

            <parameters>

              <var name="x" type="int"/>

              <var name="y" type="int"/>

            </parameters>

          </function>

 

          <function name="setSize" type="void" visibility="public">

            <parameters>

              <var name="w" type="int"/>

              <var name="h" type="int"/>

            </parameters>

          </function>

 

          <function name="setColor" type="void" visibility="public">

            <parameters>

              <var name="index" type="int"/>

              <var name="color" type="int"/>

            </parameters>

          </function>

 

          <function name="setText" type="void" visibility="public">

            <parameters>

              <var name="text" type="string"/>

            </parameters>

          </function>

 

          <function name="getText" type="string" visibility="public"/>

 

          <function name="setOrder" type="void" visibility="public">

            <parameters>

              <var name="order" type="int"/>

            </parameters>

          </function>

 

          <function name="setFocus" type="void" visibility="public">

            <parameters>

              <var name="focus" type="boolean"/>

            </parameters>

          </function>

        </functions>

      </state-machine>

 

Event

      <state-machine name="Event" access="import">

        <state-variables>

          <state-var name="sEvent" type="string"

            size="&EVENT_MAX_EVENTS;" visibility="public"/>

        </state-variables>

 

        <functions>

          <function name="setParam" type="void" visibility="public">

            <parameters>

              <var name="param" type="int"/>

              <var name="value" type="int"/>

            </parameters>

          </function>

 

          <function name="getParam" type="int" visibility="public">

            <parameters>

              <var name="param" type="int"/>

            </parameters>

          </function>

 

          <function name="fireEvent" type="void" visibility="public">

            <parameters>

              <var name="event" type="int"/>

              <var name="handler" type="string"/>

            </parameters>

          </function>

 

          <function name="fireEventNow" type="void" visibility="public">

            <parameters>

              <var name="event" type="int"/>

              <var name="handler" type="string"/>

            </parameters>

          </function>

        </functions>

 

      </state-machine>

Focus

      <state-machine name="Focus" access="import">

        <functions>

          <function name="add" type="int" visibility="public">

            <parameters>

              <var name="handler" type="string"/>

              <var name="param" type="int"/>

            </parameters>

          </function>

 

          <function name="remove" type="void" visibility="public">

            <parameters>

              <var name="id" type="int"/>

            </parameters>

          </function>

 

          <function name="setEnabled" type="void" visibility="public">

            <parameters>

              <var name="enabled" type="boolean"/>

            </parameters>

          </function>

 

          <function name="next" type="void" visibility="public">

          </function>

 

          <function name="previous" type="void" visibility="public">

          </function>

 

          <function name="set" type="void" visibility="public">

            <parameters>

              <var name="id" type="int"/>

              <var name="focus" type="boolean"/>

            </parameters>

          </function>

        </functions>

      </state-machine>

 

FunctionKey

      <state-machine name="FunctionKey" access="import">

 

        <functions>

          <function name="destroy" type="void" visibility="public">

            <parameters>

              <var name="fn" type="int"/>

            </parameters>

          </function>

 

          <function name="bind" type="void" visibility="public">

            <parameters>

              <var name="fn" type="int"/>

              <var name="handler" type="string"/>

            </parameters>

          </function>

 

          <function name="setVisible" type="void" visibility="public">

            <parameters>

              <var name="fn" type="int"/>

              <var name="visible" type="boolean"/>

            </parameters>

          </function>

 

          <function name="setText" type="void" visibility="public">

            <parameters>

              <var name="fn" type="int"/>

              <var name="text" type="string"/>

            </parameters>

          </function>

 

          <function name="push" type="void" visibility="public"/>

          <function name="pop" type="void" visibility="public"/>

        </functions>

      </state-machine>

 

 

Image

      <state-machine name="Image" access="import">

        <functions>

          <function name="init" type="void" visibility="public">

          </function>

 

          <function name="create" type="int" visibility="public">

          </function>

 

          <function name="destroy" type="void" visibility="public">

            <parameters>

              <var name="handle" type="int"/>

            </parameters>

          </function>

 

          <function name="bind" type="void" visibility="public">

            <parameters>

              <var name="handle" type="int"/>

              <var name="handler" type="string"/>

            </parameters>

          </function>

 

          <function name="setVisible" type="void" visibility="public">

            <parameters>

              <var name="handle" type="int"/>

              <var name="visible" type="boolean"/>

            </parameters>

          </function>

 

          <function name="setPosition" type="void" visibility="public">

            <parameters>

              <var name="handle" type="int"/>

              <var name="x" type="int"/>

              <var name="y" type="int"/>

            </parameters>

          </function>

 

          <function name="setSize" type="void" visibility="public">

            <parameters>

              <var name="handle" type="int"/>

              <var name="w" type="int"/>

              <var name="h" type="int"/>

            </parameters>

          </function>

 

          <function name="setColor" type="void" visibility="public">

            <parameters>

              <var name="handle" type="int"/>

              <var name="index" type="int"/>

              <var name="color" type="int"/>

            </parameters>

          </function>

 

          <function name="setText" type="void" visibility="public">

            <parameters>

              <var name="handle" type="int"/>

              <var name="text" type="string"/>

            </parameters>

          </function>

 

          <function name="setURL" type="void" visibility="public">

            <parameters>

              <var name="handle" type="int"/>

              <var name="url" type="string"/>

              <var name="srcX" type="int"/>

              <var name="srcY" type="int"/>

              <var name="width" type="int"/>

              <var name="height" type="int"/>

            </parameters>

          </function>

 

          <function name="setOrder" type="void" visibility="public">

            <parameters>

              <var name="handle" type="int"/>

              <var name="order" type="int"/>

            </parameters>

          </function>

 

          <function name="setFocus" type="void" visibility="public">

            <parameters>

              <var name="handle" type="int"/>

              <var name="focus" type="boolean"/>

            </parameters>

          </function>

        </functions>

      </state-machine>

Label

      <state-machine name="Label" access="import">

        <functions>

          <function name="create" type="int" visibility="public">

          </function>

 

          <function name="destroy" type="void" visibility="public">

            <parameters>

              <var name="handle" type="int"/>

            </parameters>

          </function>

 

          <function name="bind" type="void" visibility="public">

            <parameters>

              <var name="handle" type="int"/>

              <var name="handler" type="string"/>

            </parameters>

          </function>

 

          <function name="setVisible" type="void" visibility="public">

            <parameters>

              <var name="handle" type="int"/>

              <var name="visible" type="boolean"/>

            </parameters>

          </function>

 

          <function name="setPosition" type="void" visibility="public">

            <parameters>

              <var name="handle" type="int"/>

              <var name="x" type="int"/>

              <var name="y" type="int"/>

            </parameters>

          </function>

 

          <function name="setColor" type="void" visibility="public">

            <parameters>

              <var name="handle" type="int"/>

              <var name="index" type="int"/>

              <var name="color" type="int"/>

            </parameters>

          </function>

 

          <function name="setText" type="void" visibility="public">

            <parameters>

              <var name="handle" type="int"/>

              <var name="text" type="string"/>

            </parameters>

          </function>

 

          <function name="setOrder" type="void" visibility="public">

            <parameters>

              <var name="handle" type="int"/>

              <var name="order" type="int"/>

            </parameters>

          </function>

 

          <function name="setFocus" type="void" visibility="public">

            <parameters>

              <var name="handle" type="int"/>

              <var name="focus" type="boolean"/>

            </parameters>

          </function>

        </functions>

      </state-machine>

 

Linker

      <state-machine name="Linker" access="import">

        <variables>

          <var name="mLinkName" type="string" visibility="public"/>

          <var name="mLinkURL" type="string" visibility="public"/>

          <var name="mLinkZOrder" type="int" visibility="public"/>

          <var name="mURL" type="string" size="&URL_MAX;" visibility="public"/>

        </variables>

 

        <functions>

          <function name="add" type="int" visibility="public">

            <parameters>

              <var name="linkName" type="string" visibility="public"/>

              <var name="linkURLPrefix" type="int" visibility="public"/>

              <var name="linkURL" type="string" visibility="public"/>

              <var name="linkZOrder" type="int" visibility="public"/>

            </parameters>

          </function>

 

          <function name="onLoad" type="void" visibility="public"/>

 

          <function name="getURL" type="string" visibility="public">

            <parameters>

              <var name="url" type="int"/>

              <var name="file" type="string"/>

            </parameters>

          </function>

        </functions>

      </state-machine>

 

Menu

      <state-machine name="Menu" access="import">

        <functions>

          <function name="setAttribute" type="void" visibility="public">

            <parameters>

              <var name="attribute" type="int"/>

              <var name="color" type="int"/>

            </parameters>

          </function>

 

          <function name="getAttribute" type="int" visibility="public">

            <parameters>

              <var name="attribute" type="int"/>

            </parameters>

          </function>

 

          <function name="setVisible" type="void" visibility="public">

            <parameters>

              <var name="visible" type="boolean"/>

            </parameters>

          </function>

 

          <function name="getVisible" type="boolean" visibility="public">

          </function>

 

          <function name="center" type="void" visibility="public">

          </function>

 

          <function name="initialize" type="void" visibility="public">

            <parameters>

              <var name="handler" type="string"/>

            </parameters>

          </function>

 

          <function name="add" type="int" visibility="public">

            <parameters>

              <var name="caption" type="string"/>

              <var name="command" type="int"/>

            </parameters>

          </function>

 

          <function name="update" type="void" visibility="public">

            <parameters>

              <var name="id" type="int"/>

              <var name="caption" type="string"/>

              <var name="evt" type="int"/>

            </parameters>

          </function>

 

          <function name="highlight" type="void" visibility="public">

            <parameters>

              <var name="id" type="int"/>

            </parameters>

          </function>

 

          <function name="setPosition" type="void" visibility="public">

            <parameters>

              <var name="x" type="int"/>

              <var name="y" type="int"/>

            </parameters>

          </function>

        </functions>

      </state-machine>

RecordSet

    <state-machine name="RecordSet" access="import">

 

       <state-variables>

              <state-var name="sData"

                          type="int"

                          size="&MAX_OPEN_RECORDSETS;"

                          visibility="public"/>

       </state-variables>

 

       <variables>

              <var name="mFields" type="string"

                    size="&MAX_OPEN_RECORDSETS;,&MAX_FIELDS_PER_RECORD;"

                    visibility="public"/>

              <var name="mNumRecs"

                    type="int"

                    size="&MAX_OPEN_RECORDSETS;"

                    visibility="public"/>

       </variables>

 

       <functions>

              <function name="notify" type="void" visibility="public">

                     <parameters>

                           <var name="handle" type="int"/>

                           <var name="status" type="int"/>

                     </parameters>

              </function>

 

              <function name="EOR" type="boolean" visibility="public">

                     <parameters>

                           <var name="handle" type="int"/>

                     </parameters>

              </function>

 

              <function name="open" type="int" visibility="public">

                     <parameters>

                           <var name="notifyId" type="string"/>

                           <var name="url" type="string"/>

                           <var name="params" type="string"/>

                     </parameters>

              </function>

 

              <function name="close" type="int" visibility="public">

                     <parameters>

                           <var name="handle" type="int"/>

                     </parameters>

              </function>

 

              <function name="first" type="int" visibility="public">

                     <parameters>

                           <var name="handle" type="int"/>

                     </parameters>

              </function>

 

              <function name="moveTo" type="int" visibility="public">

                     <parameters>

                           <var name="handle" type="int"/>

                           <var name="recNum" type="int"/>

                     </parameters>

              </function>

 

              <function name="next" type="int" visibility="public">

                     <parameters>

                           <var name="handle" type="int"/>

                     </parameters>

              </function>

 

              <function name="getRecordCount" type="int" visibility="public">

                     <parameters>

                           <var name="handle" type="int"/>

                     </parameters>

              </function>

 

              <function name="getField" type="string" visibility="public">

                     <parameters>

                           <var name="handle" type="int"/>

                           <var name="fieldNum" type="int"/>

                     </parameters>

              </function>

 

       </functions>

</state-machine>

ScrollBar

      <state-machine name="ScrollBar">

        <functions> 

          <function name="initialize" type="void" visibility="public">

            <parameters>

              <var name="handler" type="string"/>

              <var name="x" type="int"/>

              <var name="y" type="int"/>

              <var name="w" type="int"/>

              <var name="h" type="int"/>

              <var name="viewPortItems" type="int"/>

              <var name="scrollBarFill" type="int"/>

              <var name="scrollBarOutline" type="int"/>

              <var name="scrollBarBg" type="int"/>

            </parameters>

          </function>

       

          <function name="setVisible" type="void"  visibility="public">

            <parameters>

              <var name="visible" type="boolean"/>

            </parameters>

          </function>

       

          <function name="setSize" type="void"  visibility="public">

            <parameters>

              <var name="size" type="int"/>

            </parameters>

          </function>

       

          <function name="update" type="void"  visibility="public">

            <parameters>

              <var name="firstViewPortItem" type="int"/>

            </parameters>

          </function>

        </functions>

      </state-machine>

 

Stack

      <state-machine name="StringStack" access="import">

        <functions>

          <function name="push" type="int" visibility="public">

            <parameters>

              <var name="item" type="string"/>

            </parameters>

          </function>

 

          <function name="pop" type="string" visibility="public"/>

        </functions>

      </state-machine>

 

      <state-machine name="IntStack" access="import">

        <functions>

          <function name="push" type="int" visibility="public">

            <parameters>

              <var name="item" type="int"/>

            </parameters>

          </function>

 

          <function name="pop" type="int" visibility="public"/>

        </functions>

      </state-machine>

 

TextBox

      <state-machine name="TextBox" access="import">

 

        <functions>

 

          <function name="initialize" type="void" visibility="public">

            <parameters>

              <var name="text" type="string"/>

              <var name="x" type="int"/>

              <var name="y" type="int"/>

              <var name="w" type="int"/>

              <var name="h" type="int"/>

              <var name="fg" type="int"/>

              <var name="bg" type="int"/>

              <var name="scrollBarFill" type="int"/>

              <var name="scrollBarOutline" type="int"/>

              <var name="scrollBarBg" type="int"/>

            </parameters>

          </function>

 

          <function name="repaint" type="void" visibility="public"/>

 

          <function name="show" type="void" visibility="public"/>

 

          <function name="hide" type="void" visibility="public"/>

 

        </functions>

      </state-machine>

 

List

      <state-machine name="List" access="import">

        <functions>

          <function name="getCurrentItem" type="int" visibility="public"/>

 

          <function name="repaint" type="void" visibility="public"/>

 

          <function name="initialize" type="void" visibility="public">

            <parameters>

              <var name="handler" type="string"/>

              <var name="x" type="int"/>

              <var name="y" type="int"/>

              <var name="w" type="int"/>

              <var name="h" type="int"/>

              <var name="itemH" type="int"/>

              <var name="fg1" type="int"/>

              <var name="bg1" type="int"/>

              <var name="fg2" type="int"/>

              <var name="bg2" type="int"/>

              <var name="highlightFg" type="int"/>

              <var name="highlightBg" type="int"/>

              <var name="scrollBarFill" type="int"/>

              <var name="scrollBarOutline" type="int"/>

              <var name="scrollBarBg" type="int"/>

            </parameters>

          </function>

 

          <function name="show" type="void" visibility="public"/>

 

          <function name="hide" type="void" visibility="public"/>

 

          <function name="setSize" type="void" visibility="public">

            <parameters>

              <var name="size" type="int"/>

            </parameters>

          </function>

 

          <function name="setItem" type="void" visibility="public">

            <parameters>

              <var name="itemNum" type="int"/>

              <var name="text" type="string"/>

              <var name="imgSize" type="int"/>

              <var name="imgSrcX" type="int"/>

              <var name="imgSrcY" type="int"/>

              <var name="imgUrl" type="string"/>

            </parameters>

          </function>

        </functions>

      </state-machine>

Message Box

In this chapter, we will learn how to use the partition mechanism of UJML as a way to develop a reusable message box component. A partition is equivalent to a class in C++ or Java, but there is a significant difference: partitions are always explicitly loaded and unloaded, instead of implicitly linked like C++ or Java classes. This “explicit linking” makes it possible to run UJML applications under very restricted memory environments. The trade-off is that developers must pay attention to the “lifetime” of partitions and make sure that unnecessary partitions don’t' use precious system resources.

 

As an example, we have created a message box partition, which displays a specified message and a set of specified buttons and lets the user select one of them.

 

Interface

Just like any other programming language, each reusable component has a well-defined interface. In the case of a UJML partition, its interface consists of imported variables and imported functions. See the definition of the interface of message box below.

 

  <variables>

    <!-- Imported variables from the parent -->

    <var name="mMessage" type="string" access="import"/>

    <var name="mItemCount" type="int" access="import"/>

    <var name="mLabel" type="string" size="&ITEM_MAX;" access="import"/>

  </variables>

  <functions>

    <function name="onSelect" type="void" access="import">

      <parameters>

        <var name="i" type="int"/>

      </parameters>

    </function>

  </functions>

 

In this example, the partition will import the message text (mMessage), the number of buttons (mItemCount), and the label of each button (mLabel[]) as variables, and the “on select” notification callback as a function.

 

Notice that there is no “displayMessageBox” API. This is because a typical UJML application is a single-purpose module and almost always has a default execution block and a default display block, which will be implicitly executed and displayed. In this case, the default execution initializes local variables, and the default display block displays the message box.

Parent Application

First, examine the source code of a sample UJML application that uses this message box partition.

 

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE ujml PUBLIC "-//UIEVOLUTION//DTD UJML 1.2//EN" "../ujml.dtd" [

  <!ENTITY ITEM_MAX "6">

]>

<ujml>

  <application>

    <state-variables>

      <state-var name="sHideMessageBox" type="boolean"/>

    </state-variables>

    <variables>

      <var name="mMessage" type="string" access="export"/>

      <var name="mItemCount" type="int" access="export"/>

      <var name="mLabel" type="string" size="&ITEM_MAX;" access="export"/>

    </variables>

    <functions>

      <function name="onSelect" type="void" access="export">

        <parameters>

          <var name="i" type="int"/>

        </parameters>

        <script>

          <!-- Hide the message box after a delay

            (so that the user can see the "selected" state) -->

          sHideMessageBox = true;

          <!-- Do something here based on the selection (the value of "i") -->

        </script>

      </function>

    </functions>

    <script>

      mMessage = "Please select Yes or No and click the FIRE button.";

      mLabel[0] = "Yes";

      mLabel[1] = "No";

      mItemCount = 2;

      _link("MessageBox", "messagebox.ujbc");

    </script>

    <states>

      <state var="sHideMessageBox">

        <transition value="true">

          <delay>250</delay>

          <execute>

            <eval>_unlink("MessageBox")</eval>

          </execute>

        </transition>

      </state>

    </states>

  </application>

</ujml>

 

Look at the top-level <script> block and notice that it initializes a set of variables (which are all marked “export”), and call a system function _link(), loading the message box partition. This is the essence of the code you need to write when using this message box partition. The system call _link() will load the specified partition in memory and execute its default execution block.

 

When the user selects one of the buttons (in this case, “yes” or “no”), the partition notifies the parent of the selection by calling the onSelect function with the index to the button (in this case, “0” or “1”).

 

The application can then use this information in deciding whether to unload the partition because it is no longer necessary. [RA2] In this example, the parent application unloads it asynchronously (with a 0.25 second delay) by setting a state variable “sHideMessageBox” (instead of the partition notifies after a certain delay and let the parent call the _unlink() function immediately) so that the parent can immediately start some operation (such as loading another partition) while the user sees the result of the selection (highlighted button).

 

Next, let’s see the source code of the “message box” partition.

Message Box Partition

First, let’s see the portion related to the default execution block and default display block.

 

  <variables>

    <!-- Imported variables from the parent -->

    <var name="mMessage" type="string" access="import"/>

    <var name="mItemCount" type="int" access="import"/>

    <var name="mLabel" type="string" size="&ITEM_MAX;" access="import"/>

    <!-- Local variables, which are initialized only once -->

    <var name="mOffsetX" type="int"/>

    <var name="mOffsetY" type="int"/>

    <var name="mCellHeight" type="int"/>

    <!-- Local variables -->

    <var name="mFocus" type="int"/>

    <var name="mCur" type="int"/>

  </variables>

  …

  <script>

    mCellHeight = &TEXTH; + &GAP; * 2;

    mOffsetX = (&SCREENW;-&CELLW;) / 2;

    mOffsetY = (&SCREENH;-mCellHeight * mItemCount) / 2

           +&TEXTH;*2 + &GAP;*2;

    mFocus = 0;

    <!-- Put the focus on the first item -->

    setItemState(0,&STATE_FOCUS;);

    <!-- Put the other items in "normal" model -->

    mCur = 0;

    while(_lt(mCur+1, mItemCount)) {

      <!-- Note that mCur wil be updated to mCur+1 within setItemState -->

      setItemState(mCur+1,&STATE_NORMAL;);

    }

    <!-- Enable input -->

    sFocus = true;

  </script>

  <display>

    <!-- Draw the background and message-->

    <box>

      <width>

        <eval>&SCREENW;</eval>

      </width>

      <height>

        <eval>&SCREENH;</eval>

      </height>

      <fg>&_COLOR_TRANSPARENT;</fg>

      <bg>&COLOR_BACKGROUND;</bg>

    </box>

    <box>

      <x>&GAP;</x>

      <y>&GAP;</y>

      <width><eval>&SCREENW;-&GAP;*2</eval></width>

      <height><eval>&TEXTH;*4+&GAP;*2+&GAP;*2</eval></height>

      <fg>&_COLOR_TRANSPARENT;</fg>

      <bg>&COLOR_BUTTONB;</bg>

      <multi-label>

        <text><eval>mMessage</eval></text>

        <x>&GAP;</x>

        <y>&GAP;</y>

        <width><eval>&SCREENW;-&GAP;*4</eval></width>

        <height><eval>&TEXTH;*4</eval></height>

      </multi-label>

    </box>

  </display>

 

The default execution block (see the <script> block) initializes three variables, mCellHeight, mOffsetX and mOffsetY based on the size of default font and the screen size. These variables will be used when the partition lays out the display elements appropriately for the device (instead of giving a fixed screen coordinate). Ignore the rest of the execution block (gray text) for now.

 

The default display block (see the <display> block), paints the background (the first <box> block), draws the box surrounding the message (the second <box> block), and the message itself (the <multi-label> block). Notice that some parameters are calculated dynamically, which is necessary to lay these elements out appropriately.

 

If the default display block is the only block displayable, the user will see a screen like this:

 

 

Now, let’s look at the section we have ignored (grayed text) along with some related code.

 

    <state-variables>

      <state-var name="sFocus" type="boolean"/>

      <state-var name="sItems" type="int" size="&ITEM_MAX;" />

    </state-variables>

    <variables>

      …

      <!-- Local variables -->

      <var name="mFocus" type="int"/>

      <var name="mCur" type="int"/>

    </variables>

    …

    <functions>

      …

      <!-- Update the state of the specified item. -->

      <function name="setItemState" type="void">

        <parameters>

          <var name="i" type="int" />

          <var name="s" type="int" />

        </parameters>

        <script>

          mCur = i;

          sItems[i] = s;

        </script>

      </function>

      …

    </functions>

    <script>

      …

      mFocus = 0;

      <!-- Put the focus on the first item -->

      setItemState(0,&STATE_FOCUS;);

      <!-- Put the other items in "normal" model -->

      while(_lt(mCur+1, mItemCount)) {

        <!-- Note that mCur wil be updated to mCur+1 within setItemState -->

        setItemState(mCur+1,&STATE_NORMAL;);

      }

      <!-- Enable input -->

      sFocus = true;

    </script>

 

 

First, it initializes mFocus with a value of 0, which indicates the index of the selected item. This means that this message box always sets the focus on the first button (if you need to specify it from the parent, turn it into an imported variable and initialize it in the parent). Then, it calls a local function, setItemState() for each button, passing STATE_FOCUS for the first button and STATE_NORMAL for the rest. The setItemState() updates the mCur, and set the specified state value to the specified index of state variable sItems[], which makes each button visible with an appropriate state (see below). Lastly, it sets the state variable sFocus to true to enable user input (see below).

 

First look at the display blocks associated with the state variable sItems[].

 

      <state var="sItems" index="*">

        <!-- Normal state: Draw only the item -->

        <transition value="&STATE_NORMAL;">

          <display>

            <box>

              <x><eval>mOffsetX</eval></x>

              <y><eval>mOffsetY+(mCellHeight+&GAP;)*mCur</eval></y>

              <expand template="tItem" />

            </box>

          </display>

        </transition>

        <!-- Focus state: Draw the item and the focus rectangle -->

        <transition value="&STATE_FOCUS;">

          <display>

            <box>

              <x><eval>mOffsetX</eval></x>

              <y><eval>mOffsetY+(mCellHeight+&GAP;)*mCur</eval></y>

              <expand template="tItem" />

              <expand template="tFocus" />

            </box>

          </display>

        </transition>

        <!-- Focus state: Draw the item in reversed color -->

        <transition value="&STATE_SELECTED;">

          <display>

            <box>

              <x><eval>mOffsetX</eval></x>

              <y><eval>mOffsetY+(mCellHeight+&GAP;)*mCur</eval></y>

              <expand template="tItemSelected" />

            </box>

          </display>

        </transition>

      </state>

 

The ”*” as the index parameter of <state> tag indicates that each button state has exactly the same set of display blocks. Each button can take one of three states, NORMAL, FOCUS, and SELECTED. The image below shows how the “Yes” button will be displayed in these three states.

 

 

Notice that those display blocks use three templates, “tItem”, tSelectedItem,” and “tFocus”. Here is the code for these templates:

 

    <templates>

      <!-- Draws the item specified mCur -->

      <template name="tItem">

        <display>

        <box>

          <width>&CELLW;</width>

          <height><eval>mCellHeight</eval></height>

          <fg>&_COLOR_TRANSPARENT;</fg>

          <bg>&COLOR_BUTTONB;</bg>

          <label>

            <text><eval>mLabel[mCur]</eval></text>

            <x><eval>(&CELLW; - _text_width(mLabel[mCur],0,0,0))/2</eval></x>

            <y><eval>&GAP;</eval></y>

            <fg>&COLOR_BUTTONF;</fg>

          </label>

        </box>

        </display>

      </template>

      <!-- Draws the item in "selected" state specified mCur -->

      <template name="tItemSelected">

        <display>

        <box>

          <width>&CELLW;</width>

          <height><eval>mCellHeight</eval></height>

          <fg>&_COLOR_TRANSPARENT;</fg>

          <bg>&COLOR_BUTTONF;</bg>

          <label>

            <text><eval>mLabel[mCur]</eval></text>

            <x><eval>(&CELLW; - _text_width(mLabel[mCur],0,0,0))/2</eval></x>

            <y><eval>&GAP;</eval></y>

            <fg>&COLOR_BUTTONB;</fg>

          </label>

        </box>

        </display>

      </template>

      <!-- Draw the focus rectangle for the item specified by mCur -->

      <template name="tFocus">

        <display>

          <box>

            <width>&CELLW;</width>

            <height><eval>mCellHeight</eval></height>

            <fg>&COLOR_BUTTONF;</fg>

            <bg>&_COLOR_TRANSPARENT;</bg>

            <box>

              <x>-1</x>

              <y>-1</y>

              <width><eval>&CELLW;+2</eval></width>

              <height><eval>mCellHeight+2</eval></height>

              <fg>&COLOR_BUTTONF;</fg>

              <bg>&_COLOR_TRANSPARENT;</bg>

            </box>

          </box>

        </display>

      </template>

    </templates>

 

The template “tItem” draws the button label (specified in mCur) in normal color, and the template “tSelectedItem” in reversed color. The template “tFocus” draws the focus rectangle.

 

As described above, setting “tFocus” to true enables user input. See the code below.

 

      <!-- Move the focus up (d=-1) or down (d=1)-->

      <function name="moveFocus" type="void">

        <parameters>

          <var name="d" type="int"/>

        </parameters>

        <script>

          <!-- Change the state of current item back to "normal" state -->

          setItemState(mFocus, &STATE_NORMAL;);

          mFocus = _min(mItemCount-1, _max(mFocus+d,0));

          <!-- Change the state of new item to "focus" state -->

          setItemState(mFocus, &STATE_FOCUS;);

        </script>

      </function>

      …

      <!-- The user can interact with this control only when sFocus is true -->

      <state var="sFocus">

        <transition value="true">

          <display>

            <box>

              <event name="onselect">

                <accelerators>

                    <key>UP</key>

                </accelerators>

                <script>

                    moveFocus(-1);

                </script>

              </event>

            </box>

            <box>

              <event name="onselect">

                <accelerators>

                    <key>DOWN</key>

                </accelerators>

                <script>

                    moveFocus(1);

                </script>

              </event>

            </box>

            <box>

              <event name="onselect">

                <accelerators>

                    <key>FIRE</key>

                </accelerators>

                <script>

                    setItemState(mFocus, &STATE_SELECTED;);

                    sFocus = false;

                    onSelect(mFocus);

                </script>

              </event>

            </box>

          </display>

        </transition>

      </state>

 

Notice that the display block of “sFocus=true” has three non-displaying <box> elements (invisible because the size is not specified), whose sole purposes are input handling. When the Up key is pressed, it calls a local function moveFocus with a value of -1. When the Down key is pressed, it calls a local function moveFocus with a value of 1. When the Fire key is pressed, it changes the state of the currently focused button to Selected, disables the input (so that the user can’t operate while the parent is performing an asynchronous operation), and notifies the parent of the selection by calling the imported function, onSelect.

 

By the way, here is a set of entities (this is equivalent to #define in C/C++).

 

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE ujml PUBLIC "-//UIEVOLUTION//DTD UJML 1.2//EN" "../ujml.dtd" [

  <!ENTITY ITEM_MAX "6">

  <!ENTITY CELLW "80">

  <!ENTITY GAP "2">

  <!ENTITY STATE_NORMAL "0">

  <!ENTITY STATE_FOCUS "1">

  <!ENTITY STATE_SELECTED "2">

  <!ENTITY COLOR_BACKGROUND "0xffcccccc">

  <!ENTITY COLOR_BUTTONB "0xffffffff">

  <!ENTITY COLOR_BUTTONF "0xff000099">

  <!ENTITY SCREENW "_getIntProperty(&_PROPERTY_INT_SCREEN_WIDTH;)">

  <!ENTITY SCREENH "_getIntProperty(&_PROPERTY_INT_SCREEN_HEIGHT;)">

  <!ENTITY TEXTH "_text_height(0,0,0)">

 

 



[1] UJML is an acronym for Ujinn Markup Language. The name is credited to the founder’s oldest son and it was originally intended to be used as the name for the company. But, the name UIEvolution stuck for the company name but Ujinn stuck for the language name. The use of the name Ujinn for the game is basically the author’s way of reusing the name Ujinn for some thing fun because Ujinn is Japanese for “Happy Genie”. The author is a sucker for self-referential systems and sentences J.

[2] Actually, it is possible to change the display without changing the value of the state variable by using the _clear_state() function.

[3] The _link() function can also be used to control z-order, but at a partition level, rather than at a <display> element level. Refer to the _link() function reference section on how to use the z-order parameter.

[4] Currently, the use of default states has no beneficial effect on the compiled byte code size because the compiler simply expands the single default state into multiple states, one for each array element. In the future, the use of default states may also reduce the byte code size by allowing the UIE Player to do the expansion rather than having the compiler do it.

[5] A future, extended version of UJML will support reusable objects written in UJML, Java, or C++.


 [RA1]How long is "too long?"

 

(JWB) Rewrote for clarity.

 [RA2]Rewrite

 

(JWB) Rewrote for clarity.