Introduction to GUI programming with TKScript and FOX
(5-February-2004). (last change: 16-Mar-2005)
<script>
<fxbutton>
<fxcheckbutton>
<fxcolordialog>
<fxcombobox>
<fxdial>
<fxfiledialog>
<fxfont>
<fx4splitter>
<fxglcanvas>
<fxgroupbox>
<fxhorizontalframe>
<fxhorizontalseparator>
<fxlabel>
<fxmainwindow>
<fxmenubar>
<fxmenucommand>
<fxmenupane>
<fxmenutitle>
<fxmessagebox>
<fxscrollbar>
<fxslider>
<fxtextfield>
<fxverticalframe>
<fxverticalseparator>
Software development and graphical user interfaces have come a long way since the very first days of 7bit ASCII/ANSI terminal emulators. The great success of the World Wide Web brought a paradigm shift concerning the way applications are being developed. The "explosion" of number crunching power for the average desktop computer in the late 80ies/early 90ies allowed for further abstraction layers to be added to existing (OS) libraries/applications. A large number of scripting and markup languages, mostly designed for special purposes (e.g. text documents, math, databases, (vector)graphics, presentations..), has emerged since then.
While application programming was mainly a "hackers" issue in the 80ies (you need to know a great deal of the underlying hardware and internal OS structures) this has changed a lot by now. Most IDEs shipped today offer some kind of interface designer which lets you insert/position/edit graphical user interfaces. Usually these interface designers will create either source code (and keep the abstract GUI representation private) or emit an XML file storing the GUI setup and names of the event callbacks that can be loaded at runtime using a "C" library (first invented 1998 by Damon Chaplin- afaik).
The "GLADE" XML GUI approach really impressed me by that time (I even hacked around on my own browser + gui toolkit by that time..but I was too unexperienced and the code was too hackish to be continued (p.s.: thanks a lot Mr. B. Horstmann!) but there are a few points that keep me from using "C" or "C++" to write GUIs:
Regarding the history of the Web, SGML/XML and markup languages in general, it is really astonishing that it took almost 7 years since the public opening of the WWW until someone released a browser that was not only capable of displaying HTML pages and providing the "usual" JS+HTML DOM but also offered the possibility to include advanced GUI elements allowing for complex GUI applications like....e.g. a browser :) YOu guessed it, I'm talking of Mozilla which was --afaik-- the first usuable (!) abd publically available browser that also featured a GUI markup language - XUL.
Well and as we all know Microsoft has not slept since then and they also built their own XML "document based" GUI toolkit (XAML and Avalon). While on the one hand it is surely true that they have not invented something really new it is on the other hand also true that they are creating a usuable and (probably working:) solution.....--but: the following issues keep me away from using this toolkit:
After this little history / view on the current situation of GUI application development I'd like to introduce you to my approach to this issue. Having worked with HTML "GUI" programming quiet a bit during the last years I always wanted to have something as easy to use as HTML+JavaScript/PHP/Perl but designed for client applications. While looking at GUI toolkit bindings for traditional scripting languages like e.g. TCL, Perl, Python or PHP I found the following issues:
Hopefully some of you who think I'm re-inventing the wheel may now understand the reasons for this project. Finally, this is distributed in the hope that it will be useful.. What I'm really looking forward to is a kind of HTML like markup language draft from e.g. the W3C staff which is designed as close to XHTML as possible. This markup language should offer the possibility to embed script statements as well as using a more abstract "ID" based message system to separate GUI and script code.
The FXML markup language, as the name implies, is designed close to the FOX toolkit. Nevertheless it is planned to include a kind of stylesheet translator once there is a real public standard for XML GUIs. This "2 stage" approach makes sense since it would otherwise be necessary to mix the FOX dependent code with the parser for a (more complex) XML GUI format.
The following section introduces you to the FOX markup language (FXML). Please note that this project is actively in development so you better check back often. There is no "stable" release available yet but you may find the current test-release quiet usuable already.
<!-- ... ---> sequence is used to exclude the given block from interpretation
STRING: string constant, e.g. 'hello' or "hello 'world !'" or 'hello "world !"'
INT: an integer, e.g. "-25", "5"
FLAGS: a string list of constants, e.g. "LAYOUT_FIX_WIDTH,LAYOUT_FIX_HEIGHT"
SCRIPT(...): a script callback, ... substitutes the current argument list
ID: reference another fxobject or set an anchor, e.g. "mymainwindow"
$<property>: substitutes an attribute of the current fx object (e.g. $text is short for fx.text)
<script body ='/*..arbitrary script code.. (no class or module statements)*/' />
<fxmainwindow flags =FLAGS parent =ID id =ID x =INT y =INT w =INT h =INT frameStyle =FLAGS layoutHints =FLAGS padLeft =INT padRight =INT padTop =INT padBottom =INT helpText =STRING tipText =STRING title =STRING > ... </fxmainwindow>
<fxlabel flags =FLAGS parent =ID id =ID x =INT y =INT w =INT h =INT frameStyle =FLAGS layoutHints =FLAGS padLeft =INT padRight =INT padTop =INT padBottom =INT helpText =STRING tipText =STRING iconPosition =FLAGS justify =FLAGS text =STRING font =ID fx.text =STRING />
<fxbutton flags =FLAGS parent =ID id =ID x =INT y =INT w =INT h =INT frameStyle =FLAGS layoutHints =FLAGS padLeft =INT padRight =INT padTop =INT padBottom =INT helpText =STRING tipText =STRING iconPosition =FLAGS justify =FLAGS text =STRING font =ID onLeftBtnRelease =SCRIPT() fx.text =STRING />
<fxfont flags =FLAGS parent =ID id =ID size =INT h =INT face =STRING weight =FLAGS slant =FLAGS encoding =FLAGS setWidth =FLAGS />
<fxtextfield flags =FLAGS parent =ID id =ID x =INT y =INT w =INT h =INT frameStyle =FLAGS layoutHints =FLAGS padLeft =INT padRight =INT padTop =INT padBottom =INT helpText =STRING tipText =STRING cols =INT text =STRING font =ID onVerify =SCRIPT(String _text) onKeyPress =SCRIPT(int _code, int _state) onKeyRelease =SCRIPT(int _code, int _state) fx.text =STRING />
<fxslider flags =FLAGS parent =ID id =ID x =INT y =INT w =INT h =INT frameStyle =FLAGS layoutHints =FLAGS padLeft =INT padRight =INT padTop =INT padBottom =INT helpText =STRING tipText =STRING headSize =INT slotSize =INT tickDelta =INT value =INT inc =INT lo =INT hi =INT onLeftBtnRelease =SCRIPT(int _value) onMotion =SCRIPT(int _value) fx.value =INT; fx.lo =INT; fx.hi =INT; />
<fxdial flags =FLAGS parent =ID id =ID x =INT y =INT w =INT h =INT frameStyle =FLAGS layoutHints =FLAGS padLeft =INT padRight =INT padTop =INT padBottom =INT helpText =STRING tipText =STRING revolutionIncrement =INT notchSpacing =INT notchOffset =INT dialStyle =FLAGS value =INT lo =INT hi =INT onLeftBtnRelease =SCRIPT(int _value) onMotion =SCRIPT(int _value) fx.value =INT; fx.lo =INT; fx.hi =INT; />
<fxscrollbar flags =FLAGS parent =ID id =ID x =INT y =INT w =INT h =INT frameStyle =FLAGS layoutHints =FLAGS padLeft =INT padRight =INT padTop =INT padBottom =INT helpText =STRING tipText =STRING page =INT inc =INT value =INT hi =INT onLeftBtnRelease =SCRIPT(int _value) onMotion =SCRIPT(int _value) fx.value =INT; fx.hi =INT; />
<fxgroupbox flags =FLAGS parent =ID id =ID x =INT y =INT w =INT h =INT frameStyle =FLAGS layoutHints =FLAGS padLeft =INT padRight =INT padTop =INT padBottom =INT helpText =STRING tipText =STRING text =STRING groupBoxStyle =FLAGS font =ID > ... </fxgroupbox>
<fxhorizontalframe flags =FLAGS parent =ID id =ID x =INT y =INT w =INT h =INT frameStyle =FLAGS layoutHints =FLAGS padLeft =INT padRight =INT padTop =INT padBottom =INT helpText =STRING tipText =STRING > ... </fxhorizontalframe>
<fxverticalframe flags =FLAGS parent =ID id =ID x =INT y =INT w =INT h =INT frameStyle =FLAGS layoutHints =FLAGS padLeft =INT padRight =INT padTop =INT padBottom =INT helpText =STRING tipText =STRING > ... </fxverticalframe>
<fxhorizontalseparator flags =FLAGS parent =ID id =ID x =INT y =INT w =INT h =INT frameStyle =FLAGS layoutHints =FLAGS padLeft =INT padRight =INT padTop =INT padBottom =INT helpText =STRING tipText =STRING />
<fxverticalseparator flags =FLAGS parent =ID id =ID x =INT y =INT w =INT h =INT frameStyle =FLAGS layoutHints =FLAGS padLeft =INT padRight =INT padTop =INT padBottom =INT helpText =STRING tipText =STRING />
<fxmenubar flags =FLAGS parent =ID id =ID />
<fxmenupane flags =FLAGS parent =ID id =ID > ... </fxmenupane>
<fxmenucommand flags =FLAGS parent =ID id =ID label =STRING onLeftBtnRelease =SCRIPT() onLeftBtnPress =SCRIPT() />
<fxmenutitle flags =FLAGS parent =ID id =ID label =STRING />
<fxglcanvas flags =FLAGS parent =ID id =ID x =INT y =INT w =INT h =INT frameStyle =FLAGS layoutHints =FLAGS padLeft =INT padRight =INT padTop =INT padBottom =INT helpText =STRING tipText =STRING onPaint =SCRIPT() onMotion =SCRIPT(int _x, int _y) onKeyPress =SCRIPT(int _code, int _state) onKeyRelease =SCRIPT(int _code, int _state) onLeftBtnPress =SCRIPT() onLeftBtnRelease =SCRIPT() timeout =INT fx.timeout =INT FXGLCanvas::setTimeout(int _ms) FXGLCanvas::startTimer(int _ms) FXGLCanvas::stopTimer() />
<fxmessagebox flags =FLAGS parent =ID id =ID x =INT y =INT w =INT h =INT frameStyle =FLAGS layoutHints =FLAGS padLeft =INT padRight =INT padTop =INT padBottom =INT helpText =STRING tipText =STRING caption =STRING message =STRING int FXMessageBox::execute() - create/show/run modal />
<fxfiledialog flags =FLAGS parent =ID id =ID x =INT y =INT w =INT h =INT frameStyle =FLAGS layoutHints =FLAGS padLeft =INT padRight =INT padTop =INT padBottom =INT helpText =STRING tipText =STRING title =STRING - popup window title filename =STRING pattern =STRING - e.g. "all files (*)\nscripts (*.tks)\nFXML files (*.fxml)" patternList =STRING currentPattern =INT -e.g. 2=FXML files directory =STRING itemSpace =INT fileBoxStyle =FLAGS selectMode =FLAGS showReadOnly =BOOL int execute() - create/show/runModal.. returns true or false.. />
<fx4splitter
flags =FLAGS
parent =ID
id =ID
x =INT
y =INT
w =INT
h =INT
frameStyle =FLAGS
layoutHints =FLAGS
padLeft =INT
padRight =INT
padTop =INT
padBottom =INT
helpText =STRING
tipText =STRING
hSplit =INT - fraction 0..10000
vSplit =INT - fraction 0..10000
splitterStyle =FLAGS
barSize =INT
expanded =INT - 0..3 or -1==all
/>
<fxcolordialog
flags =FLAGS
parent =ID
id =ID
x =INT
y =INT
w =INT
h =INT
frameStyle =FLAGS
layoutHints =FLAGS
padLeft =INT
padRight =INT
padTop =INT
padBottom =INT
helpText =STRING
tipText =STRING
title =STRING - popup window title
rgba =INT - #AARRGGBB
opaqueOnly =BOOL
/>
<fxcheckbutton flags =FLAGS parent =ID id =ID x =INT y =INT w =INT h =INT frameStyle =FLAGS layoutHints =FLAGS padLeft =INT padRight =INT padTop =INT padBottom =INT helpText =STRING tipText =STRING iconPosition =FLAGS justify =FLAGS text =STRING font =ID checkButtonFlags =FLAGS onLeftBtnRelease =SCRIPT(int _check) fx.text =STRING fx.check =INT />
<fxcombobox flags =FLAGS parent =ID id =ID x =INT y =INT w =INT h =INT frameStyle =FLAGS layoutHints =FLAGS padLeft =INT padRight =INT padTop =INT padBottom =INT helpText =STRING tipText =STRING font =ID text =STRING editable =INT numColumns =INT numVisible =INT currentItem =INT comboStyle =FLAGS onLeftBtnRelease =SCRIPT(int _item) fx.text =STRING fx.currentItem =INT fx.editable =INT - combobox is editable (1) or just the default text is displayed (0) fx.items =STRING - e.g. "one\ntwo\nthree" getItemText(int _i) - return text string for item # _i />
<script> element can be used to embed arbitrary script code (e.g. helper functions).
fx. prefix in this documentation can be changed using scripts.
Upon initialization of the tkfox plugin, a static FXApp object is created in the global namespace. It currently has the following methods:
Each (composite) element (i.e. buttons,sliders etc..) which has been assigned an unique name can be accessed in the (temporary) module namespace with the prefix fx_.
Each callback function has an implicit variable called fx which stores a reference to the current FXButton,FXTextField.. element.
Object members are either addressable by using the "fx.<member>" syntax.
use fox;
function CreateRunGUIFromString(Pointer _s) {
String s<=_s;
FXPage p<=new FXPage;
TreeNode troot<=s.parseXML();
if(troot)
{
trace "tree has "+troot.numNodes+" nodes.";
p.createFromTree(troot);
FXApp.create();
FXApp.run();
}
}
String s; s.loadLocal("test.fxml",1);
CreateRunGUIFromString(s);
// :)
font face="Courier New"><fxmainwindow><fxbutton text="&Hello, world." onLeftBtnRelease="FXApp.stop();"/></fxmainwindow>
- creates a top level window with a button in it that reads "hello, world". Clicking the button exits the application.
<fxmainwindow>
<fxfont id=fnText face=helvetica size=12/>
<fxlabel id=lbValue text="n/a" font="fnText"/>
<fxtextfield id=tfValue cols=32 flags="TEXTFIELD_INTEGER" text="0"
onKeyPress='
int i=tcint(fx.text);
fx_lbValue.text="Current value: "+i;
fx_sbValue.value=sqrt(i);
'
/>
<fxslider id=sbValue lo=0 hi=100 value=1 layoutHints="LAYOUT_FILL_X,LAYOUT_FIX_HEIGHT" h=21
onMotion='
int i=_value*_value;
fx_lbValue.text="Current value: "+i;
fx_tfValue.text=i;
'
/>
<fxbutton font="fnText" text="Quit" layoutHints="LAYOUT_RIGHT" onLeftBtnRelease='FXApp.stop();'/>
</fxmainwindow>
- creates a top level window with a label, a textfield, a slider and an exit-button. The label and textfields displays the square of the current slider value. Entering text in the textfield in turn updates the slider (square root of text typecasted to int). Clicking the button exits the application.
<fxmainwindow id=main>
<fxbutton text="Open error dialog.." layoutHints="LAYOUT_FILL_X"
onLeftBtnRelease='FXMessageBox_error(fx_main, 0, "caption","..error..");'
/>
<fxbutton text="Open warning dialog.." layoutHints="LAYOUT_FILL_X"
onLeftBtnRelease='FXMessageBox_warning(fx_main, 0, "caption","..warning..");'
/>
<fxbutton text="Open information dialog.." layoutHints="LAYOUT_FILL_X"
onLeftBtnRelease='FXMessageBox_information(fx_main, 0, "caption","..information..");'
/>
<fxbutton text="Open question dialog.." layoutHints="LAYOUT_FILL_X"
onLeftBtnRelease='FXMessageBox_question(fx_main, 0, "caption","..question..");'
/>
<fxbutton text="Quit" layoutHints="LAYOUT_FILL_X" onLeftBtnRelease='FXApp.stop();'/>
</fxmainwindow>
- A simple FXMessageBox test application.
<fxmainwindow id=main>
<fxfiledialog id=myfile title="Open file"
filename ="test.txt"
pattern ="*.txt"
/>
<fxfiledialog id=mymultifile title="Open files"
filename ="test.txt"
pattern ="*.txt"
patternList ="All files (*)\nText files (*.txt)\nPNG Images (*.png)\n
TKScript source (*.tks)\nTKScript pak files (*.tkx)\nFXML files (*.fxml)\nC++ source (*.cpp)\nC++ header (*.h)"
currentPattern =4
selectMode ="SELECTFILE_MULTIPLE"
/>
<fxbutton text="Open single file dialog.." layoutHints="LAYOUT_FILL_X"
onLeftBtnRelease='
if(fx_myfile.execute())
{
trace "selected file=\""+fx_myfile.filename+"\"";
trace "selected dir=\""+fx_myfile.directory+"\"";
}
'
/>
<fxbutton text="Open multi file dialog.." layoutHints="LAYOUT_FILL_X"
onLeftBtnRelease='
if(fx_mymultifile.execute())
{
trace "selected files:";
StringArray fs<=fx_mymultifile.filenames;
trace " num="+fs.numElements;
String s; foreach s in fs trace " \""+s+"\"";
}
'
/>
<fxbutton text="Quit" layoutHints="LAYOUT_FILL_X,LAYOUT_FILL_Y" onLeftBtnRelease='FXApp.stop();'/>
</fxmainwindow>
- Demonstrates single + multi select file dialogs.
<fxmainwindow id=w w=512 h=384>
<fxverticalframe>
<fx4splitter id=split layoutHints="LAYOUT_FILL_X,LAYOUT_FILL_Y">
<fxgroupbox text="sector 0" layoutHints="LAYOUT_FILL_X,LAYOUT_FILL_Y">
<fxbutton text="expand sector 1" layoutHints="LAYOUT_FILL_X,LAYOUT_FILL_Y"
onLeftBtnRelease='fx_split.expanded=1;'
/>
</fxgroupbox>
<fxgroupbox text="sector 1">
<fxbutton text="expand sector 2" layoutHints="LAYOUT_FILL_X,LAYOUT_FILL_Y"
onLeftBtnRelease='fx_split.expanded=2;'
/>
</fxgroupbox>
<fxgroupbox text="sector 2">
<fxbutton text="expand sector 3" layoutHints="LAYOUT_FILL_X,LAYOUT_FILL_Y"
onLeftBtnRelease='fx_split.expanded=3;'
/>
</fxgroupbox>
<fxgroupbox text="sector 3">
<fxbutton text="expand sector 0" layoutHints="LAYOUT_FILL_X,LAYOUT_FILL_Y"
onLeftBtnRelease='fx_split.expanded=0;'
/>
</fxgroupbox>
</fx4splitter>
<fxbutton text="reset splitter" layoutHints="LAYOUT_FILL_X"
onLeftBtnRelease='fx_split.expanded=-1; fx_split.hSplit=5000;fx_split.vSplit=5000;'
/>
</fxverticalframe>
</fxmainwindow>
- Demonstrates the FX4Splitter layout manager.
<fxmainwindow id=main>
<fxverticalframe>
<fxcolordialog id=dia title="Select color"
<!-- rgba ="#ffd78623"-->
opaqueOnly =0
/>
<fxbutton text="Open color dialog.." layoutHints="LAYOUT_FILL_X"
onLeftBtnRelease='
if(fx_dia.execute()) {
trace "selected color=\""+fx_dia.rgbaString+"\"";
fx_html.text=fx_dia.rgbaString;
}
else
trace "colordialog aborted.";
'
/>
<fxhorizontalframe>
<fxlabel text="HTML color (#aarrggbb):"/>
<fxtextfield id=html text="#ffd78623" onVerify='fx_dia.rgba=_text;'/>
</fxhorizontalframe>
<fxbutton text="Quit" layoutHints="LAYOUT_FILL_X,LAYOUT_FILL_Y" onLeftBtnRelease='FXApp.stop();'/>
</fxverticalframe>
</fxmainwindow>
- Demonstrates the FXColorDialog widget.
<fxmainwindow> <fxcheckbutton text="check it out" check=1 checkButtonStyle="" boxColor="" onLeftBtnRelease='trace "button has been "+(_check?"checked":"unchecked");'/> </fxmainwindow>
- Demonstrates the FXCheckButton widget.
<script body='
function PickCategory(int _cat) {
trace "You picked category #"+_cat+" (\""+fx_main.getItemText(_cat)+"\")";
if(_cat)
fx_sub.items="red\ngreen\nblue\nalpha";
else
fx_sub.items="one\ntwo\nthree\nfour";
fx_sub.editable=true;
}
function PickItem(int _item) {
trace "You picked item #"+_item+" (\""+fx_sub.getItemText(_item)+"\")";
}
'/>
<fxmainwindow>
<fxverticalframe>
<fxgroupbox text="ComboBox-Test">
<fxhorizontalframe>
<fxverticalframe frameStyle="FRAME_NONE">
<fxlabel text="Category:"/>
<fxlabel text="Item:"/>
</fxverticalframe>
<fxverticalframe frameStyle="FRAME_NONE">
<fxcombobox id="main" numColumns="18" items="Numbers\nColor channels"
onListClicked='PickCategory(_item);'/>
<fxcombobox id="sub" numVisible="4" numColumns="18" text="Pick a category first!"
editable="0" onListClicked='PickItem(_item);'
/>
</fxverticalframe>
</fxhorizontalframe>
</fxgroupbox>
<fxbutton layoutHints="LAYOUT_FILL_X" text="&Quit" onLeftBtnRelease='FXApp.stop();'/>
</fxverticalframe>
</fxmainwindow>
- Demonstrates the FXComboBox widget.
Final notice: This project is discontinued. It should merely be considered as a proof-of-concept experiment although currently my work focuses on creating a new GUI addon. stay tuned :)
OpenGL is © 1992-2004 by Silicon Graphics and the OpenGL consortium <http://www.sgi.com/software/opengl/license.html
Mozilla is © 1998-2004 The Mozilla Organization / Netscape <http://www.mozilla.org/about/
XAML/Avalon is © 2002-2004 by Microsoft <http://www.microsoft.com
FOX is © 1997-2004 by Jeroen van der Zijp <http://www.fox-toolkit.org>
GLADE is © 1998-2004 by Damon Glade <http://glade.gnome.org>
TKScript, YAC and FXML are © 2001-2004 by Bastian Spiegel <http://tkscript.de>