home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Gold Fish 3
/
goldfish_volume_3.bin
/
files
/
dev
/
e
/
amigae
/
src
/
tools
/
easygui
/
easygui.doc
< prev
Wrap
INI File
|
1994-11-08
|
17KB
|
448 lines
[Here's the new EasyGUI! This version is really usable.
NOTE: read this first before recompiling any old EasyGUI stuff!]
Introducing:
E A S Y G U I
An interface builder for E, with the following highlights:
- It's totally Font-Sensitive
- It's Resizable
- It's Self-Organising, i.e. it arranges gadgets
- It's more StyleGuide compliant than your granny
- It's Fast and Flexible
- It's relatively small, needs no extra external libraries
- And above all: It's extremely easy to use!!!
changes from the v3.1a version (features):
- new functions to access/modify gadgets while GUI is active
- easily add gadtools menus
- more complete docs
- works better with multiple simultanuous EasyGUIs
- LISTV, MX and CYCLE have an extra "current" parameter now
- STR now takes an _estring_ as value (make sure you change
this in old sources!)
bugs:
- current gadget values would reset upon resize
- would open in middle of screen instead of visible part
- many tiny bugs removed
+---------------------------------------------------------------+
| 1. EasyGUI Intro |
+---------------------------------------------------------------+
EasyGUI takes the form a module file that needs to be included into your
E source (needless to say, it needs v2.04/v37 of the OS). The most simple
form of constructing a GUI consist of calling the function easygui() with
a (possibly nested) E list which describes your GUI. just to show how Easy,
try this source:
MODULE 'tools/EasyGUI'
PROC main() IS easygui('um,...',[BUTTON,0,'Ok!'])
This'll open a window with just one gadget in it, and wait for the
user to push it. If easygui() can't get what it wants, it'll start
throwing around exceptions, so we'll probably need an exception
handler to be able to inform the user properly (see below).
The first arg of easygui() is the window title, the second one is the
GUI description. The form of these desciptions is quite simple: It's a
list with as first element the type of gadget, the second is called an
action value (more later), and the rest is gadget-specific.
To be able to build GUI's outof more components than just one gadget,
one can group gadgets with a ROW and a COL list:
[COLS,
[BUTTON,1,'Ok'],
[BUTTON,0,'Cancel']
]
This'll create a new group, consisting of two gadgets next to each other.
COLS and ROWS groups are like a single gadget, i.e. you can easily put them
into other groups, to create GUI's of infinite complexity.
Other Grouping functions are EQCOLS and EQCOLS, which try to align gadgets
in a group.
[BEVEL,a] will put a bevel box around a, whatever it is (a gadget, a group...),
and BEVELR is the recessed version.
Other elements of groups are mostly gadgets, which have a specific #of
arguments. [If the number of arguments is incorrect, EasyGUI will raise
the "Egui" exception.]
The first element is always the type of gadget to create (see below). The
second element is always something called an "actionvalue", which tells
EasyGUI what needs to be done when the user interacts with the gadget.
All elements after that are gadget specific.
An action value may be:
- a small positive integer (0-1000). If the user selects this gadget,
EasyGUI will close the window, and return that value as returnvalue
from the easygui() call. This is meant for "Ok" / "Cancel" type buttons.
- a pointer to an "actionfunction". If the user selects this gadget,
EasyGUI will call the function with as arguments depending on the
type of gadget (for example a slider will get it's current value).
After the actionfunction returns, EasyGUI continues processing messages
from the GUI.
example:
...[BUTTON,{load},'Load'],...
PROC load(info) IS WriteF('You pushed the "Load" button!\n')
the value of `info' is explained below. Or:
DEF s[100]:STRING
...[STR,{str},'input:',s,50,4],
[CYCLE,{cycle},'choose:',['Yep','Nope',NIL],1],...
PROC str(info,news) IS WriteF('the new string is: \s\n',news)
PROC cycle(info,newc) IS WriteF('the new choice is: \d\n',newc)
In the action function, you can store the new value, however EasyGUI keeps
track of it itself too: In the case of the STR above it will StrCopy() new
values into your estring, so it automatically has the correct value after
closing. In the case of all other gadgets it stores the new (integer) value
in the list (so that `1' in `CYCLE' may become `0'). This has the added
benefit that windows that are opened and then closed again will automatically
start with the current values.
The easygui() function:
easygui(windowtitle,gui,info=0,screen=0,textattr=0,newmenus=0)
`info' may be ANY value, and is always passed as first arg to the
action functions. For example if you write a prefsrequester, this may
be the the prefs OBJECT. Your actionfunctions then have a simple task
changing the value of the element in question.
`screen' is an optional screen to open on. if not present, EasyGUI opens
on the current public screen.
`textattr' is a fontdescription, and is best left NIL. If NIL, EasyGUI
will pick the font the user selected as "screenfont" in fontprefs.
`newmenus' can be a newmenus structure (as in gadtools.library). EasyGUI
will then automatically attach it to the window and arrange any messages.
You can give the same actionvalues as gadgets in the newmenu.userdata,
and the actionfunction can be the same as for a `BUTTON', i.e.:
[...,2,0,'Load','l',0,0,{load},...]:newmenu
can use the same load() as in the example further above.
+---------------------------------------------------------------+
| 2. Gadgets |
+---------------------------------------------------------------+
general format: [NAME,action,text,...]
in {}: which direction it may resize.
* = not implemented or things missing.
+ = a value that can be affected lateron with the set#? functions
Each gadget shows the gadget template, explanation, the form of the
actionfunction, and a typical example).
[BUTTON,action,intext]
buttonaction(info)
example: [BUTTON,0,'Cancel']
[CHECK,action,righttext,checkedbool+,lefttextbool]
checkedbool = whether gadget should initially be check-marked
checkaction(info,checkedbool)
example: [CHECK,{case},'Ignore case',TRUE,FALSE]
[INTEGER,action,lefttext,num+,relsize]
num = initial value
integeraction(info,newnum) {x}
example: [INTEGER,{v},'int:',5,3]
[LISTV,action,textabove,relx,rely,execlist+,readbool,selected,current+]
execlist = ptr to an execlist. (see tools/constructors.m)
readbool = whether listview is read-only
*selected = 0=none, 1=read, 2=strgad (fill in 0 for compat.)
listviewaction(info,num_selected) {x,y}
example: [LISTV,0,NIL,5,5,filenamelist,0,NIL,0]
[MX,action,abovetext,nil_term_elist,lefttextbool,current]
mxaction(info,num_selected)
example: [MX,{v},NIL,['One','Two','Three',NIL],FALSE,1]
[CYCLE,action,lefttext,nil_term_elist,current]
cycleaction(info,num_selected)
example: [CYCLE,{v},'choose:',['Yep','Nope',NIL],1]
[PALETTE,action,lefttext,depth,relx,rely]
depth = 1..8, number of bitplanes this color is for
paletteaction(info,colour) {x,y}
example: [PALETTE,{v},'color:',3,5,2]
[SCROLL,action,isvert,total+,top+,visible+,relsize]
total = resolution of scroller
top = current top represented
visible = current
scolleraction(info,curtop) {x|y}
example: [SCROLL,{v},FALSE,10,0,2,2]
[SLIDE,action,lefttext,isvert,min,max,cur+,relsize,levelformat]
min,max = value range of slider
cur = current value
levelformat = string that shows levelformat, example '%2ld'. leave
a large amount of spaces left in lefttext for this.
slideraction(info,cur) {x|y}
example: [SLIDE,0,'Colors:',FALSE,1,8,3,5,'']
[STR,action,lefttext,initial+,maxchars,relsize]
initial = initial string contents: NOTE: HAS TO BE AN ESTRING!
maxchars = max #of chars for string
stringaction(info,newstring) {x}
example: [STR,0,'Pattern',s,200,5] (DEF s[100]:STRING)
[TEXT,text,lefttext,borderbool,relsize] {x}
borderbool = whether or not a recessed bevelbox should be placed around 'text'
example: [TEXT,'Selected Fonts',NIL,FALSE,3]
*[NUM,int,lefttext,borderbool,relsize] {x}
*[RENDER,actionr,actionp,x,y] -> in fontunits
renderrefresh(x,y,xs,ys)
renderpress(x,y) -> relative to topcorner
*[RENDERFIXED,actionr,actionp,x,y] -> in pixels
renderfixedrefresh(x,y)
renderfixedpress(x,y)
[SBUTTON] {x}
same as button, only now resizes.
[BAR], [SPACE] {x,y}, [SPACEH] {x}, [SPACEV] {y}
BAR places a nice divider-bar between gadgets/groups. Whether it's
horizontal or vertical depends on which group it is in. SPACE/SPACEH/SPACEV
do nothing, they only eat up space. This can be very handy in GUI design,
they act like a spring between elements (do not use them on the borders of a
GUI, only in the middle).
See the example GUI's how to use these.
#?text:
(where #? is left/right etc.): a text to place next to the gadget. often
is allowed to be NIL.
relsize,relx,rely:
generally gadgets will automatically get a size depending on a number of
factors, but relsize allows the programmer to give a minimum size for
certain gadgets, thereby sizing a whole group. If other gadgets already
account for the minimum size, this one can safely be set to a low value
such as 2. All these sizes are calculate in terms of the hight of the font.
nil_term_elist:
a nil-terminated E list, such as ['One','Two','Three',NIL]
isvert:
TRUE if gadget needs to be vertical, horizontal by default.
+---------------------------------------------------------------+
| 3. How Layout Works |
+---------------------------------------------------------------+
EasyGUI works by automatically layouting the gadgets on the
screen. In a first pass, it will compute the minimum size for
each element: for gadgets this involves the size of fonts and
various other things. For a ROWS list, for example, it will take
the width of the biggest gadget as its width, and computes its
height by adding the heights of all other gadgets. For EQROWS
this is slightly more complicated: EasyGUI also computes a "middle"
for various gadgets, often between the text that denotes what a
gadget is about and the gadget itself. It then tries to align
all of these. for EQCOLS it simply tries to make all columns
equal width.
In a second pass, EasyGUI assigns the final coordinates to all
gadgets. Important to notice is that in a GUI often there is
more space available than is required for a gadget, for example
the gadget above it in a ROWS environment is much wider. Also, the
user may have resized the window. To do something useful with this
space, EasyGUI looks at which gadgets can do something useful
with extra space, such as LISTV, or STR, SCROLL etc. This process
of granting extra space propagates through ROWS/COLS, which act
as gadgets themselves. Gadgets like BUTTON don't benefit from more
space, so they give that away to their neighbours.
+---------------------------------------------------------------+
| 4. Advanced features |
+---------------------------------------------------------------+
Throwing exceptions from actionfunctions
----------------------------------------
is allowed: EasyGUI will catch it, close the window properly,
and then ReThrow() it.
exceptions raised by EasyGUI itself:
"MEM" -- no mem
"GUI" -- for things like CreateGadgetA, OpenWindowTagList etc.
"GT" -- couldn't open gadtools.library
"bigg" -- for "BIG Gui": interface is calculated to be bigger than the screen.
generally you should keep gui's small, so that they still fit on
640x200 topaz screens. If the user runs Times/30 on a screen this size,
he probably knows he has a problem.
"Egui" -- a design error: most probably handed over a list to dogui()
that was either to long or too short
<other> -- Raise()ed by own function
You can also use a "quit" exception or somesuch if you need to quit from an
actionfunction (i.e. other than with an actionvalue).
Multiple Windows
----------------
The simplest use of EasyGUI is just by calling easygui(). You can however
open any number of windows, and check messages for all of them.
guiinit(windowtitle,gui,info=0,screen=0,textattr=0,newmenus=0)
guimessage(guihandle)
cleangui(guihandle)
Call guiinit() for each window (exactly the same arguments as easygui(). Then,
keep calling guimessage() for each of them, when messages arive. you can
close them again with cleangui(), for example when a gui returns a
positive integer (the actioncode). Negative integers signal that it simply
finished processing all messages, but no need to close the window yet.
example of usage of these three function (= definition easygui())
EXPORT PROC easygui(windowtitle,gui,info=NIL,screen=NIL,textattr=NIL,newmenus=NIL) HANDLE
DEF gh=NIL:PTR TO guihandle,res=-1
gh:=guiinit(windowtitle,gui,info,screen,textattr,newmenus)
WHILE res<0
Wait(gh.sig)
res:=guimessage(gh)
ENDWHILE
EXCEPT DO
cleangui(gh)
ReThrow()
ENDPROC res
the object you get has some handy field in there: the window in question,
and the sigmask (i.e. Shl(1,sigbit)), if you want to do a proper Wait()
(OR them).
OBJECT guihandle
wnd:PTR TO window
sig:LONG
ENDOBJECT /* SIZEOF=64 */
^^^^private!
if you want to draw into `wnd': stdrast is automatically set to the last
EasyGUI opened.
Multiple copies of a GUI
------------------------
if your app allows to have multiple copies of the _same_ gui open
at the same time (for example if you open windows recursively, or
you use the multiple window technique described above to open more
instances of one GUI), you might need to dynamically allocate the GUI
description, because of the way dynamically computed values are
put into static E lists. A GUI desciption with [] lists is static,
i.e. only allocated once. Adding NEW to all of them is hard to deallocate,
and this is where disposegui() comes in. To safely use this feature,
allocate ALL lists belonging to the GUI desciption dynamically with
NEW [...] (this does not include lists such as the one used for the
various labels in CYCLE-gadgets).
easygui('Bla',
gui:=NEW [ROWS,
NEW [STR,{str},'input:',s,50,4],
NEW [CYCLE,{cycle},'choose:',['Yep','Nope',NIL],1]])
disposegui(gui)
Call disposegui() with the top-level list. on each gadget-list (i.e.
NEW [CYCLE,...]) it will simply call FastDisposeList(), on COLS and
ROWS etc. it will first deallocate each element recursively.
Manipulating Gadgets
--------------------
[currently, this assumes you're using the DIY version of EasyGUI (as
described under "multiple windows".]
You might need to modify gadgets while a GUI is active, for example
to set a slider when a corresponding integer gadget is modified by
the user, or to change the contents of a listview.
You can denote gadgets to change by simply storing their addresses,
i.e.:
[COLS,
mygad:=[CHECK,....],
...
]
Now you can use `mygad' with some of the functions below. Note that,
of course `mygad' isn't a gadget, but it helps EasyGUI to find the
real gadget.
setscrollvisible(gh,gad,visible)
setscrolltop(gh,gad,top)
setscrolltotal(gh,gad,total)
setlistvselected(gh,gad,active)
setlistvlabels(gh,gad,labs)
setinteger(gh,gad,new)
setcycle(gh,gad,active)
setmx(gh,gad,active)
setstr(gh,gad,new)
setslide(gh,gad,new)
setcheck(gh,gad,bool)
for all these: `gh' is the gui you're talking about (as returned from
guiinit()), `gad' is a value that denotes the gadget as described above.
the third value is whatever you're changing about the gadget. Note that
in doing so, you need to respect usual restrictions on gadtools gadgets,
for example setlistlables() requires that you first set it to -1, then
modify the list, and put it back.
realgadget:=findgadget(gh,list)
allows you to find the gadget address, for all those modifications
that aren't possible with the above set#? functions. It returns an intuition
gadget structure. Note that preferably you will want to use the set#?
functions, as these cooperate with EasyGUI very well (in keeping
track of the current value, for example: setstr() also copies the new
value to the estring you attached to the gadget).
+---------------------------------------------------------------+
| 5. bugs/future |
+---------------------------------------------------------------+
bugs:
- method of displaying slider values not bulletproof
- sometimes problems with finding correct size for gadgets with a
lefttext in an EQROWS
future:
- implement render spaces for draw/edit purposes
- implement gadgets with images on them
- implement a BOOPSI door
- easy blocking (for now, look at the JRH's RKRM intuition/requesters_alerts/
blockinput.e if you need this)
----------------------------------------------------------------
General advice: try out and modify the examples. Sometimes something won't work,
but EasyGUI is flexible enough that at least one way of arranging groups etc.
will give you a nice GUI :-). If you need more power than EasyGUI currently
gives, you'll have to use MUI/BGUI/WhatEver instead.
Wouter