Language 3: FLIRT
LimeLife
didn’t know it at the time, but they needed a technology to manage
their application porting and they readily accepted it when offered.
Writing code in J2ME (Java) and then recoding it in Brew (C ) is
tedious and the multiple screen sizes of phones make generating code
for each screen size effortful. An API doesn’t work so well when your
code needs to be in two different languages.
Build, buy, or API?
Some mobile application companies have written standardized UI
generators and some have written things like a scripting language that
rehosts Java just so they can download their code as data at runtime.
The primary commercial candidate for the J2ME/Brew network portable
language was the UIEvolution system. But it was expensive, only handled
scripting (not layout), and generated code similar in size to Java
itself. I thought we could do much better, hence I built FLIRT.
FLIRT
is not just a scripting language repackaging java/brew code into a
one-to-one-correspondence. FLIRT integrates scripting and display in a
language designed specifically for cell-phones. Size is the paramount
concern. While Java is designed to produce highly compact code, FLIRT
script saves 50% over Java code. Java tries to be a universal language
across machines big and small. But cell phones imply significant
limitations and a well-designed scripting language can take advantage
of that.
Since a major “reusable” component of
mobile applications is code to manage the UI, I organized FLIRT around
a state description of each screen. The state describes what softkeys
are to be used, what script code to execute in response to incoming
events, and what graphical elements to draw on the screen in what
order.
The first design decision I made was to
restrict the range of all script arguments to one byte, using
indirection as needed to access tables of things taking more than a
byte (e.g., 32-bit integers, colors, fonts, strings, graphics).
The
second design decision was to eliminate local variables. All user data
is normally kept in global two-dimensional arrays, one for bytes,
32-bit integers, strings, and graphics. The [0][] is for transient
unrelated values, while other first dimensions hold sets of related
information. For example all the strings of a menu would be in, for
example, strings[3][]. So a script element defining a menu could refer
to that using the single byte value 3. Of course the language allowed
you to automatically label dimensions so you never used the number.
For example, defining your global strings array might look like this:
This
would define stringSets[0] to be anonymously labeled but its members
have labels (labels are named followed by colon) and sometimes initial
values. StringSets[1] is called sTRENDS_MENU and is a list of
anonymously named strings which would be the names of menu choices.
The
user script will have named intial values for the data arrays, as well
as arrays describing fonts, colors, and softkeys. All of which can then
be accessed using a single byte value (name) used in the appropriate
instruction or element context.
For example, a
TextRect display element can be thought of as a subroutine call to
display a set of text somewhere on the screen. It’s actually more than
that, because it is not code but data to be interpreted, and the data
can be modified by the scripting language. This means the script can
scroll the content, move it around, change what text it points to, etc.
Be that as it may, this call takes 15 arguments to completely define
the element. Since each argument is always a byte, it means it takes 15
bytes (half the size it would take J2ME to do it as a function call).
These bytes tell it:
-
the kind of element (TextRect)
-
an ID label so that script can interact with it and other elements can lay themselves out relative to it
-
a bunch of modifier bits to control its behavior (including left, right
or center alignment, should scrolling it wrap around the ends or pin)
-
which text string to display
-
at what location relative to some other element on the screen (which
means it automatically adjusts to different size screens when the other
elements are smaller or larger graphics).
-
how much size to reserve for this element in x and y
-
what font to use (a font includes custom graphics for regular and highlight state)
-
the current scroll and limit (text too big for a field can be scrolled by line or by page)
The
third design decision was to make screen layout relative. FLIRT display
elements are designed to be laid out in a relative manner, autolaying
themselves out as appropriate for different size screens and data.
Events
include softkey events, keypad events, and pseudo events the user can
create. Since there are no local variables, there are no “calling
arguments” either, except that you set up some reserved spots in the
global data arrays to be used in the call. Hence triggering an event
and making a subroutine call to event code of some state use the same
mechanism.
FLIRT acts as our common library
resource, in that sections of it can be compiled in or out, including
custom fonts and textwrapping, networking, sorting, and RMS (file)
access. Interface elements include textblocks, horizontal and vertical
menus, marquees, graphics, scrollbars, screen keyboard, clipping areas,
and camera abilities.
The fourth design decision
was to make scrolling a fundamental metaphor over all graphical
elements. In combination with scripts Flirt supports scrolled graphics
(including animation), scrolled text, scrolled shapes (e.g., progress
bars). Scrollbars following a display element automatically reflected
the scroll state of that element, allowing you to have classic vertical
scrollbars or horizontal scrollbase with text labels (like captions)
for the current state of a scroll.
LimeLife
products, being aimed at the women’s market, have a heavy amount of
esthetic design, so simple menus just are plain unacceptable. The
emulator screen at right is of LimeLife’s Instyle product. The menu is
not a single menu element, it is composed of display elemets including
character elements (the numbers in blue), text elements (the menu
entries in black), and colored shape elements (highlights and separator
bars). A router element controls behavior among the display elements.
It names a list of other elements and as you scroll the router, you
change which other element is selected (highlighted) by it.
This
screen with its routers takes about 70 individual elements to compose
it (where half of them are control or non-visible layout elements like
an element which evenly spaces out a collection of elements
vertically). The screen will automatically re-lay itself out if the
graphic size or font size changes (or even if the graphic is missing
because the network connection failed). It works on all screen sizes
and on BREW or J2ME phones.
The script code for
this state reacts to arrows, numbers for quick access, softkeys, and
caches the main graphic in RMS if it can. The entire state description
is 1093 bytes (script + display elements), which makes it one of the
more complex states (since the average is 219 bytes per state). The
product has 83 states taking 18,169 bytes (that’s display elements and
script, and does not include text or images).
Having
code and display information in one place (the state description) is a
lot handier than our old J2ME-coded applications, where display code
went in one big switch statement and control code went in different
switch. It was hard to see how one related to the other. ( J2ME size
issues incline one to write non-object oriented code in massive switch
statements.)
Because all screen elements and
script code is just “data”, new or revised states can be downloaded
from the server if desired.
New product mockups
can be rapidly created with FLIRT. It took a week to create a
full-fledged visual demo of our second Flirt-based product (visual in
that the controls only acted to take you from screen to screen and had
no other functionality). When the BREW FLIRT engine was built, three
completely different FLIRT-based products ran on it immediately. One
merely took the data files built for the J2ME engine and ran them on
the BREW one. Autolayout screens and all scripts ran correctly.
Conclusion:
Writing a new scripting language is a major undertaking, which goes far
beyond merely coding it up. But when you have a really compelling
reason to do so, you are not reinventing the wheel. You are creating a
way to concisely express your thoughts in a new language. And language
is what gives humans enormous leverage over the universe.
About the Author
I am both a consultant and an employee (in all cases, as a
long-distance telecommuter, living in Hawaii in the past to
Gloucester, UK by the time this article is uploaded). Want to reach
me? gowilcox@gmail.com or look at my resume on Gamasutra.
|