Introducing: E A S Y G U I v3.3b.2 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 - The layout-engine is user-extendable with PLUGINs - And above all: It's extremely easy to use!!! +---------------------------------------------------------------+ | 0. History | +---------------------------------------------------------------+ As of v3.3a, maintained by Jason R. Hulance, who you can contact at the addresses given in the Beginner's Guide to Amiga E. (Email: jason@fsel.com) Changes from the v3.3b version: - Fixed bug: STR, INTEGER and CHECK gadgets' action functions were not getting passed the correct new value parameter. - Fixed bug: LISTV wasn't always wide enough for label. - Added EG_HIDE tag to enable the GUI to be created with a hidden (closed) window. - Changed blockwin()/unblockwin() so they now nest properly. Changes from the v3.3a version: - MAJOR CHANGE: made the main functions use a taglist for their optional arguments (it was getting a bit ridiculous...). This gives a bit more flexibility for the programmer and makes for much more readable code, so I'm afraid it was inevitable. To ease the change-over a bit there is an example module which gives the old style versions of the functions (just for EasyGUI.m; you can do your own for EasyGUI_lite.m). - MAJOR ADDITION: support for multi-window GUIs. This is via the multiinit(), addmultiA(), multimessage(), cleanmulti() and checkmulti() functions. The new scheme shares message ports, and efficiently dispatches on the message data. This is *massively* more efficient than using the old scheme (OR-ing the signal masks of each window), and effectively removes the limit of windows that a GUI can use. - Changed 'info' default to -1, which now gets translated to the guihandle. This helps a lot with multi-window GUIs. - Changed 'data' default to -1, which now gets translated to the gadget (not the *real* gadget, but the one that can be used with the setXXX() functions). - Changed the default screen to be the *default* public screen (rather than specifically Workbench). - Changed AppWindow gadget location code to make it more robust, and now includes the gadget label as "hittable". - Added a short description of the examples to this doc. - Added a reference to the guihandle to each PLUGIN so they can more easily access the GUI window and use the GUI manipulation functions. - Added closewin()/openwin() to enable the window to be temporarily closed and opened. These are most useful for iconification (see the iconify and toolify PLUGINs). Note that this new feature may break old PLUGINs that assume the GUI window will never change. - Added extra 'maxw' and 'maxh' parameters to make window open with maximum width/height (taking into account position, too). - Added extra 'wintype' parameter to give control over window borders. - Added some more changeXXX() functions, and changed (sorry!) the other changeXXX() functions to make them more uniform. - Added extra checking in message loop to ignore stray IDCMP_MOUSEMOVE messages. This is needed because GadTools doesn't identify the gadget if you use GACT_FOLLOWMOUSE, so your PLUGIN finds it hard to trap this message properly. [Problem reported by Ralph Wermke ] - Fixed bug in key handling code for the SLIDER gadget. [Bug reported first by Fred van der Zee ] - Fixed an old bug: guihandles weren't deallocated by cleangui(). - Fixed an old bug: some messages weren't getting replied. - Fixed an old bug: window wasn't being centred on screen if smaller than display (and may not have been openable). - Minor changes to the layout of a couple of gadgets. - Made gadtoolsbase and workbenchbase public, mainly so that PLUGINs can use them (if you have a GUI open then they must be valid). - Made things more friendly for multi-tasking: gh.wnd is NIL when the GUI is in an unmodifiable state. - Made awprocs use a copy of the appmessage, mainly so that they can do things like manipulate the GUI without problems. - Made a third version of EasyGUI. This is EasyGUI_debug.m, which is the same as EasyGUI.m except that it will print a short diagnostic message before raising an exception. This should be used when developing your program to help track down the actual reason for a fault. Changes from the v3.2e version: - Lots of minor fixes to most gadgets. - Added conditional resizing. - Added (optional) data field for all gadgets. - Added Style Guide compliant keyboard shortcuts. - Added extra arguments to some PLUGIN methods. - Added simple mechanism for PLUGINs to use GadTools gadgets (gtrender()). - Added AppWindow support for main window, *BUTTON, LISTV, STR and PLUGINs. - Added a lot of GUI manipulation functions. - Made an EasyGUI_lite version without a lot of features. changes from the v3.2a version: - now supports PLUGINs for unlimited complex gui's!!! (check section 5) - added settext() and setnum() - fixed NUM gadget - added topaz-fallback version - bug: rendered into the border when used with sysihack - bug: could cause enforcer hits when some strings were NIL - bug: dealocation of resources in wrong order caused problems changes from the v3.1a version: - 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 (change this in old sources!) - bug: current gadget values would reset upon resize - bug: 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 easyguiA() with a (possibly nested) E list which describes your GUI. just to show how Easy, try this source: MODULE 'tools/EasyGUI' PROC main() IS easyguiA('um,...',[BUTTON,0,'Ok!']) This'll open a window with just one gadget in it, and wait for the user to push it. If easyguiA() 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 easyguiA() 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. IF you get strange layouts using these, you should try grouping subgroups before putting them in a larger 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 easyguiA() call. This is meant for "Ok" / "Cancel" type buttons. - a pointer to an "action function". 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. If you want to close the GUI from an action function then you can call the quitgui() function, which raises a "QUIT" exception (be careful to not handle this exception in your code -- just ReThrow() it if your action function has an exception handler). The single argument to quitgui() (which defaults to 0) will be returned from the easyguiA() call, in the same way that action values are. The definition of quitgui() is: PROC quitgui(ret=0) IS Throw("QUIT",ret) There are two new, optional arguments to action functions: `qual' and `data'. If you wish to use these arguments your action function must include them at the beginning (and you must have a `data' argument if you want to use the `qual' argument). For instance, a button action function can be defined in any of these three ways: PROC action(info) IS WriteF('info=$\h\n', info) PROC action(data,info) IS WriteF('info=$\h,data=$\h\n', info,data) PROC action(qual,data,info) IS WriteF('info=$\h,data=$\h,qual=$\h\n', info,data,qual) (This scheme gives *full* compatibility with the old EasyGUI action functions, whilst still allowing the new features to be used.) The `data' argument is the optional data field specified in the gadget. If it is not specified or it's -1 then the actual data value that is used is the gadget list pointer (for use with the setXXX() functions). You could use it, instead, to pass a pointer to an object specific to the gadget. The action function can then just call a method of that object. This allows the same action function to be used by a number of different gadgets, with the specific action being carried out via the data element (since the data can be different for each gadget). The `qual' is basically the qualifier part of the intuimessage used to indicate gadget clicks. Use the IEQUALIFIER_XXX constants from the module 'devices/inputevent' to see which qualifiers were pressed when the gadget was clicked. For example: PROC buttonaction(qual,data,info) IF qual AND (IEQUALIFIER_LSHIFT OR IEQUALIFIER_RSHIFT) PrintF('You were pressing a shift key when you clicked on me!\n') ENDIF ENDPROC The easyguiA() function: easyguiA(windowtitle,gui,tags=NIL) `tags' is a taglist which can use the following tags: EG_INFO the data may be ANY value, and is passed as an 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. (If this is -1 or not present, then it the info used will be the 'guihandle' value for this GUI. See Chapter 4.) EG_SCRN the data is an optional screen ptr to open on. if NIL or not present EasyGUI opens on the default public screen (usually Workbench). EG_FONT the data is a fontdescription (i.e., a `textattr'). If NIL or not present, EasyGUI will use the same font as the screen (for Workbench this is the font the user selected as "screenfont" in fontprefs). EG_MENU the data is 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.: [...,NM_ITEM,0,'Load','l',0,0,{load},...]:newmenu can use the same load() as in the example further above (the optional `qual' and `data' arguments [see below] will always be NIL). [The constants NM_ITEM, etc., are from the module 'libraries/gadtools'.] EG_GHVAR the data is the address of a LONG variable which gets a copy of the guihandle (see Multiple Windows, below). You just specify something like {gh} instead of having to use guiinitA(), etc. Note that you can also get the guihandle by using the new default for the `info' of a GUI (as described above). EG_AWPROC the data is a function to be called when the user drops icons on the window (and no gadget handles the drop). By default, this is NIL and this means that the window will not to be made into an AppWindow. If it's *not* NIL then the window *will* be an AppWindow, and the *BUTTON, LISTV and STR gadgets will get their awprocs called if icons are dropped directly on them (PLUGIN gadgets can also react to AppWindow messages). If the drop does not land on a gadget with an awproc then this awproc (for the whole window) will be called. (See the examples.) awprocs are like action functions and can be defined in one of two ways: PROC awproc(info,awmsg:PTR TO appmessage) PROC awproc(data,info,awmsg:PTR TO appmessage) `info' is the GUI info, `awmsg' is a *copy* of the appmessage and `data' is the optional data field for the gadget (or the `plugin_object' for PLUGINs, and NIL for the window one). EG_TOP, EG_LEFT the data define the top-left coordinate of the window. If either of them is -1 (the default), then the window will be centred on that axis (in the visible part of the screen). EG_MAXW, EG_MAXH the (boolean) data specify whether to try make the window size maximal in width or height (based on screen size), rather than the default of minimal (based on the GUI). If you specify a position then the maximal size of that axis will be the remaining part of the screen. So, on a screen of width 640, if you specify EG_LEFT of 100 then the maximum width the window can be is 540. EG_WTYPE the data is a constant to dictate the window decoration (i.e., what happens with the borders). The choices are: WTYPE_SIZE the default, which gives you all the standard gadgets and a size gadget at the bottom. WTYPE_NOSIZE omits the size gadget. WTYPE_BASIC just has a simple border around the window. WTYPE_NOBORDER has (suprisingly) no border. Be careful with these last two options as there is no close gadget for the user to click -- you must provide them with your own method of closing the GUI (a menu item?). (Note: the window title is ignored with the last two types, since there's no title bar.) EG_CLOSE the data is an action value/function used when the user clicks the close gadget on the GUI window. This is most useful for multi-window GUIs (see Multiple Windows), but it can be useful to prompt the user to confirm before quitting the GUI. The action value will be returned from easyguiA()/guimessage()/multiloop()/multimessage(), as usual. The action function can be defined in one of two ways: PROC closeproc(info) PROC closeproc(mh:PTR TO multihandle,info) `info' is the GUI's info parameter (see above), and `mh' is the multi-window handle (see Multiple Windows) or NIL if it's a single window GUI. EG_CLEAN the data is a function to be called when the GUI is destroyed (e.g., by calling cleangui()). This is most useful for multi-window GUIs (see Multiple Windows), but it can generally be useful for cleaning up the GUI's PLUGINs (see Chapter 5). The function will be called *after* the window and the GUI has been closed and deallocated, so, for example, the PLUGINs will no longer be on screen. It should be defined like this: PROC cleanproc(info) `info' is the GUI's info parameter (see above). Note: if guiinitA() or addmultiA() cause an exception then this function will *not* be called. This makes it easier to setup a GUI (see the examples). EG_HIDE the (boolean) data specifies whether the GUI window is initially hidden (closed). The default is FALSE; if you specify TRUE then you can use openwin() to show the window when you want it to be seen. Note: this tag is a bit useless for a normal call to easyguiA() as there's little chance of it receiving any GUI messages if the window is closed (so the Wait() will never return). +---------------------------------------------------------------+ | 2. Gadgets | +---------------------------------------------------------------+ general format: [NAME,action,text,...] in {}: which direction it may resize. + = a value that can be affected lateron with the set#? functions * = optional value (can be omitted) Each gadget shows the gadget template, explanation, the form of the actionfunction, and a typical example. The optional arguments to action functions (qual and data) are described in the above chapter. [BUTTON,action,intext,data*,key*,awproc*,disabled+*] data = user data value passed to action function key = keyboard shortcut (presses button) awproc(data*,info,awmsg) = AppWindow proc (see above) disabled = whether this gadget is disabled or not buttonaction(qual*,data*,info) example: [BUTTON,0,'Cancel'] [CHECK,action,righttext,checkedbool+,lefttextbool,data*,key*,disabled+*] checkedbool = whether gadget should initially be check-marked key = keyboard shortcut (toggles check) disabled = whether this gadget is disabled or not checkaction(qual*,data*,info,checkedbool) example: [CHECK,{case},'Ignore case',TRUE,FALSE] [INTEGER,action,lefttext,num+,relsize,data*,key*,disabled+*] num = initial value key = keyboard shortcut (activates gadget) disabled = whether this gadget is disabled or not integeraction(qual*,data*,info,newnum) {x} example: [INTEGER,{v},'int:',5,3] (Note: see discussion of getinteger() in chapter 4 below.) [LISTV,action,textabove,relx,rely,execlist+,readbool,selected,current+, data*,key*,awproc*,disabled+*] execlist = ptr to an execlist. (see tools/constructors.m) readbool = whether listview is read-only (action is ignored if it is!) selected = 0=none, 1=highlight/show selected key = keyboard shortcut (unshifted->select next, shifted->prev) awproc(data*,info,awmsg) = AppWindow proc (see above) disabled = whether this gadget is disabled or not listviewaction(qual*,data*,info,num_selected) {x,y} example: [LISTV,0,NIL,5,5,filenamelist,0,NIL,0] (Note: disabling doesn't work under V37.) [MX,action,righttext,nil_term_elist,lefttextbool,current, data*,key*,disabled+*] (Note: under v37 the righttext is spaced correctly but not rendered.) key = keyboard shortcut (unshifted->next choice, shifted->prev, wraps) disabled = whether this gadget is disabled or not mxaction(qual*,data*,info,num_selected) example: [MX,{v},NIL,['One','Two','Three',NIL],FALSE,1] (Note: disabling doesn't work under V37.) [CYCLE,action,lefttext,nil_term_elist,current,data*,key*,disabled+*] key = keyboard shortcut (unshifted->next choice, shifted->prev, wraps) disabled = whether this gadget is disabled or not cycleaction(qual*,data*,info,num_selected) example: [CYCLE,{v},'choose:',['Yep','Nope',NIL],1] [PALETTE,action,lefttext,depth,relx,rely,current+,data*,key*,disabled+*] (Note: the current field is new to v3.3a and is _not_ optional, so any existing v3.2e sources using a PALETTE gadget will need a small edit.) depth = 1..8, number of bitplanes this color is for key = keyboard shortcut (unshifted->next pen, shifted->prev, wraps) disabled = whether this gadget is disabled or not paletteaction(qual*,data*,info,colour) {x,y} example: [PALETTE,{v},'color:',3,5,2] [SCROLL,action,isvert,total+,top+,visible+,relsize,data*,key*,disabled+*] total = resolution of scroller top = current top represented visible = current key = keyboard shortcut (unshifted->increment, shifted->decrement) disabled = whether this gadget is disabled or not scolleraction(qual*,data*,info,curtop) {x|y} example: [SCROLL,{v},FALSE,10,0,2,2] [SLIDE,action,lefttext,isvert,min,max,cur+,relsize,levelformat, data*,key*,disabled+*] 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. key = keyboard shortcut (unshifted->increment, shifted->decrement) disabled = whether this gadget is disabled or not slideraction(qual*,data*,info,cur) {x|y} example: [SLIDE,0,'Colors:',FALSE,1,8,3,5,''] [STR,action,lefttext,initial+,maxchars,relsize, over*,data*,key*,awproc*,disabled+*] initial = initial string contents: NOTE: HAS TO BE AN ESTRING! maxchars = max #of chars for string over = overwrite mode boolean (default is FALSE, i.e., insert mode) key = keyboard shortcut (activates gadget) awproc(data*,info,awmsg) = AppWindow proc (see above) disabled = whether this gadget is disabled or not stringaction(qual*,data*,info,string) {x} (Note: action function is passed the E-string, not the gadget's buffer.) example: [STR,0,'Pattern',s,100,5] (DEF s[100]:STRING) (Note: see discussion of getstr() in chapter 4 below.) [TEXT,text,lefttext,borderbool,relsize] {x} borderbool = whether or not a recessed bevelbox is placed around 'text' example: [TEXT,'Selected Fonts',NIL,FALSE,3] [NUM,int,lefttext,borderbool,relsize] {x} borderbool: as TEXT example: [NUM,123,'num:',TRUE,5] [SBUTTON,action,intext,data*,key*,awproc*,disabled+*] {x} same as button, only now resizes horizontally. [PLUGIN,action,plugin_object,isgt*,awproc*] isgt = whether this PLUGIN is made from GadTools gadgets (see chapter 5) awproc(plugin_object*,info,awmsg) = AppWindow proc (see above) pluginaction(info,plugin_object) (see separate chapter 5 on using/implementing these). [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). #?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 calculated in terms of the _height_ of the font. Always try out your GUI with different fonts. For example if you design a gui that only just fits horizontally with an 8 point font on 640x200, when run on with a 13 point font on 640x512, the gui will be quite a bit bigger horizontally, but the screen isn't, so it won't fit. (see also: the "bigg" exception below). 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. key: All keyboard shortcuts are optional (so marked with a * in the following descriptions). If specified then they must be lowercase letters ("a" to "z"). However, the shifted and unshifted keystrokes are *both* handled, in the way described by the Style Guide (and this is briefly outlined for each gadget). Also, if a key is specified then an "_" in the gadget label text indicates the character in the label which is to be underscored (and this ought to be the same letter as the key). If a key is not specified then any "_"s in the label are not special. (If an action function is invoked as a result of a key press then the `qual' will always be 0.) disabled: If this optional field is specified then the gadget can be disabled and enabled using the setdisabled() function (see below, chapter 4), and the current status of the gadget will be recorded in this field, too. The following constants/enumerations are defined in order to make referencing/creating the lists for each gadget a bit more readable. EXPORT ENUM BEV_GUI=1, BUT_ACT=1, BUT_TXT, BUT_DATA, BUT_KEY, BUT_APPW, BUT_DIS, CHK_ACT=1, CHK_TXT, CHK_VAL, CHK_LEFT, CHK_DATA, CHK_KEY, CHK_DIS, INT_ACT=1, INT_TXT, INT_VAL, INT_REL, INT_DATA, INT_KEY, INT_DIS, LST_ACT=1, LST_TXT, LST_RELX, LST_RELY, LST_LIST, LST_RO, LST_SHOW, LST_CURR, LST_DATA, LST_KEY, LST_APPW, LST_DIS, MX_ACT=1, MX_TXT, MX_LIST, MX_LEFT, MX_CURR, MX_DATA, MX_KEY, MX_DIS, CYC_ACT=1, CYC_TXT, CYC_LIST, CYC_CURR, CYC_DATA, CYC_KEY, CYC_DIS, PAL_ACT=1, PAL_TXT, PAL_DEP, PAL_RELX, PAL_RELY, PAL_CURR, PAL_DATA, PAL_KEY, PAL_DIS, SCR_ACT=1, SCR_VERT, SCR_TOTL, SCR_TOP, SCR_VIS, SCR_REL, SCR_DATA, SCR_KEY, SCR_DIS, SLI_ACT=1, SLI_TXT, SLI_VERT, SLI_MIN, SLI_MAX, SLI_CURR, SLI_REL, SLI_FMT, SLI_DATA, SLI_KEY, SLI_DIS, STR_ACT=1, STR_TXT, STR_STR, STR_MAX, STR_REL, STR_OVR, STR_DATA, STR_KEY, STR_APPW, STR_DIS, TXT_VAL=1, TXT_TXT, TXT_BORD, TXT_REL, NUM_VAL=1, NUM_TXT, NUM_BORD, NUM_REL, PLG_ACT=1, PLG_OBJ, PLG_GT, PLG_APPW +---------------------------------------------------------------+ | 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 | +---------------------------------------------------------------+ Different versions of EasyGUI ----------------------------- For those who care more about saving a few KB than the extra functionality, there are two different versions: 1) EasyGUI_lite.m This does not have multi-window support, keyboard support for gadgets, AppWindow support or GUI manipulation functions (i.e., dynamically changing a GUI, window blocking and gadget disabling). Current code size: 17324 bytes. 2) EasyGUI.m The full version, with nothing taken out. Current code size: 23968 bytes. There's also a third version of EasyGUI which is just a variant of EasyGUI.m: this is a debug version, EasyGUI_debug.m, which gives some more information about an error before raising an exception (so you shouldn't really distribute programs compiled with this module -- it's just for development purposes). Throwing exceptions from actionfunctions ---------------------------------------- is allowed: EasyGUI will catch it, close the window properly, and then ReThrow() if it wasn't a "QUIT" exception. If it was "QUIT" then the exceptioninfo value will be the return value of the easyguiA()/guimessage() or multiloop()/multimessage() functions, just like the way actionvalues work (except for the slight difference in a multi-window GUI, as noted below). 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 -- Raise()ed by own function Multiple Windows ---------------- There are two ways of handling multiple windows in EasyGUI. The first has really been superseded by the second, though. 1) The simplest use of EasyGUI is just by calling easyguiA(). You can however open any number of windows, and check messages for all of them. guiinitA(windowtitle,gui,tags=NIL) guimessage(guihandle) cleangui(guihandle) Call guiinitA() for each window (exactly the same arguments as easyguiA(). 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. Note: it is *not* safe to call cleangui() from *any* action function (except as noted below, in the description of cleanmulti()). (If you want to close the GUI prematurely, you can use the closewin() function.) example of usage of these three function (= definition easyguiA()) EXPORT PROC easyguiA(windowtitle,gui,tags=NIL) HANDLE DEF gh=NIL:PTR TO guihandle,res=-1 gh:=guiinitA(windowtitle,gui,tags) WHILE res<0 Wait(gh.sig) res:=guimessage(gh) ENDWHILE EXCEPT DO cleangui(gh) ReThrow() ENDPROC res the object you get has some handy fields in there: the window in question `wnd' (or NIL if it's currently closed), the sigmask `sig' (i.e., _not_ the signal bits) if you want to do a proper Wait() (OR them), the GUI's info and the handle for the multi-window group `mh' (or NIL if this is a single GUI) [see below for more details on this]. OBJECT guihandle /* Public (read-only) parts: */ wnd:PTR TO window sig:LONG info mh:PTR TO multihandle -> See below... ENDOBJECT The GUI's info is provided to make retrieving the info from the guihandle easier (in an EG_CLEAN proc, for instance). If you want to draw into `wnd': stdrast is automatically set to the last EasyGUI opened. `wnd' can also be used a flag to indicate when it is safe to manipulate the GUI (to help with multi-tasking): when it's NIL it's not safe to modify the GUI directly, unless you've been passed the window pointer (e.g., render() and clear_render()). The setXXX() functions are always safe. Note: `wnd' can change due to closewin()/openwin() calls which mean that the GUI may be in different windows during its life. 2) But starting in v3.3b, there's a much more efficient (and simpler) way of handling multiple-window GUIs. This uses the functions multiinit(), addmultiA(), multiloop() (which uses multimessage()) and cleanmulti(), in roughly the same way as the single window versions. It's all based around a `multihandle': OBJECT multihandle /* Public (read-only) parts: */ sig:LONG opencount:LONG ENDOBJECT multiinit() addmultiA(multihandle,windowtitle,gui,tags=NIL) multiloop(multihandle) multimessage(multihandle) cleanmulti(multihandle) multiinit() is used to initialise a new group of GUIs and produce a new `multihandle'. It takes no arguments. You must call this function first and use the multihandle with the other functions to refer to your group of GUIs. You can still refer to each individual GUI using a guihandle (see below). A quick example to show how simple the new multi-window scheme is: PROC multiplewindows() HANDLE DEF mh=NIL, gh1:PTR TO guihandle, gh2:PTR TO guihandle,res mh:=multiinit() gh1:=addmultiA(mh,'GUI One',[BUTTON,{but1},'Press Me']) gh2:=addmultiA(mh,'GUI Two',[BUTTON,{but2},'Press Me']) -> Could add more GUIs... res:=multiloop(mh) EXCEPT DO cleanmulti(mh) ENDPROC addmultiA() is very much like guiinitA(). It creates a new window with your specified GUI and returns the guihandle. You can call this function even from action functions, when the GUI group is active. addmultiA(mh:PTR TO multihandle,windowtitle,gui,tags=NIL) The extra parameter is `mh' to indicate the group to which this window will be added. If the tag EG_CLOSE is not specified or the data is an action value (between 0 and 1000) then the GUI will be destroyed and removed from the group (the guihandle will now be invalid) and the action value will be returned (from multimessage()) -- just like a normal action value. If it is an action function then the window is *not* closed and the action function is called. As stated above, you define such an action function in one of two ways: PROC closeproc(info) PROC closeproc(mh:PTR TO multihandle,info) `info' is the GUI's info parameter and `mh' is the multihandle for this group. Use `mh' to find the `opencount' -- the number of GUIs with open windows. This count will include this GUI so if it's equal to one then you know this is the last open window in the group. Your action function may want to prompt the user and then close the window (using closewin()), destroy the GUI and remove it from the group (using cleangui()), or leave the window open. multiloop() is defined in terms of multimessage() and opencount, and is similar to the definition of easyguiA(). EXPORT PROC multiloop(mh:PTR TO multihandle) DEF res=-1 WHILE res<0 EXIT mh.opencount=0 Wait(mh.sig) res:=multimessage(mh) ENDWHILE ENDPROC res The signal mask for waiting for messages is `sig' in the multihandle. You can use this for more complex Wait()s, and then call multimessage() to handle any messages that may have arrived for any of the GUIs. Any action values generated by a GUI (or via quitgui()) are returned by multimessage() and hence terminate multiloop(). The above example calls multiloop() only once, so an action value will eventually destroy the whole group (by calling cleanmulti()). Note: a real action value will cause the GUI that produced it to be closed via cleangui(), but a value returned through use of quitgui() will not -- this is because if you've called quitgui() then you can just as easily write code to close the window (or whatever) in the same action function. By using the `opencount', the loop will be terminated when there are no more open GUI windows. Alternatively, if you are using your own Wait() you might want to check that there are no GUIs left (even ones with closed windows). To this end there's the function multiempty() which returns TRUE if and only if there are no GUIs in the group. (Note: if there are only GUIs left with no window then the simple Wait() will never return!) cleanmulti() destroys all GUIs in the group (whether they have an open or closed window) and deallocates the multihandle. It is *not* safe to use the multihandle or any of the guihandles after this. So, in particular, it is *not* safe to call cleanmulti() from *any* action function. It is, however, safe to call cleangui() on GUIs in the group, if you are using cleanmulti() to do the final cleanup (rather than calling cleangui() on remembered guihandles). To help with manipulating a group there are the multiexists() and multiforall() functions, which work in a very similar way to ForAll() and Exists(). multiforall(varaddr,mh:PTR TO multihandle,expr) multiexists(varaddr,mh:PTR TO multihandle,expr) `varaddr' is the address of a (global) guihandle variable, `mh' identifies the GUI group and `expr' is a quoted expression to be evaluated. multiforall() will evaluate the expression for every GUI in the group (whether its window is open or closed) and return TRUE if the expression is TRUE (non-zero) for all GUIs, else it will return FALSE. multiexists() will stop at the first GUI for which the expression is TRUE, and return the value of the expression. Unlike Exists(), you are guaranteed that the variable pointed to by `varaddr' will contain the guihandle of the corresponding GUI (or NIL if none were found). So, you can use multiexists() to find GUIs. Note: be careful what the expression does with the guihandle and the GUI group; it is quite safe to cleangui() on the supplied guihandle, but it is not safe to affect any other GUI in this way. Any GUIs added to the group by the expression will not be part of the forall or exists (for those who can't live without knowing: the list is explored from head to tail, and new GUIs are added to the head; so the last GUI considered will be the oldest GUI). For example, you can print the title of each GUI with an open window: multiforall({gh},mh,`IF gh.wnd THEN WriteF('\s\n',gh.wnd.title) ELSE 0) A final reminder: `varaddr' must be the *address* of a *global* variable (and so must all the variables used in the quoted expression). Useful note: as long as a guihandle is valid (i.e., as long as cleangui() has not been called on it), the normal GUI functions checkgui() and guimessage() will actually work on the whole group that a GUI is in. (Note: the proper, safe functions to use for multi-window GUIs are checkmulti() and multimessage(), respectively.) 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). easyguiA('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 -------------------- [This assumes you've got the guihandle by using the ghaddr argument of easyguiA(), the DIY version of EasyGUI (as described under 'Multiple Windows'), or you're using the new default for the 'info' of a GUI.] 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. setcheck(gh,gad,bool) setinteger(gh,gad,new) setlistvlabels(gh,gad,labs) setlistvselected(gh,gad,active) setlistvvisible(gh,gad,vis) setmx(gh,gad,active) setcycle(gh,gad,active) setpalette(gh,gad,colour) setscrolltop(gh,gad,top) setscrolltotal(gh,gad,total) setscrollvisible(gh,gad,visible) setslide(gh,gad,new) setstr(gh,gad,new) settext(gh,gad,newtext) setnum(gh,gad,newnum) for all these: `gh' is the gui you're talking about (as returned from guiinitA()/addmultiA(), or via the `ghaddr' argument of easyguiA() or the new default for the 'info' of a GUI), `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 setlistvlabels() requires that you first set it to -1, then modify the list, and put it back. setlistvvisible() will try to make the indicated item in the LISTV visible. setlistvselected() will make a new selection and try to make it visible (and -1 turns off the selection). setdisabled(gh,gad,disabled=TRUE) This applies to all gadgets, but works only if you've actually specified a value (TRUE or FALSE) for the optional `disabled' field for the gadget. 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). While there is at least one GUI still allocated the `gadtoolsbase' and `workbenchbase' are valid, so you can quite easily manipulate gadgets in action functions using GadTools functions. (If you do open either of these libraries, be careful not to overwrite these variables with NIL! That is, use an automatic exception on OpenLibrary() or otherwise check the return value before assigning it.) getstr(gh,gad) getinteger(gh,gad) These return and store the current value of a STR or INTEGER gadget (where gh and gad are as described above for the setxxx() functions). They are useful because Intuition only sends a message (and so causes the current values to be stored) when the user hits the return or tab key in these gadgets. So, if you really want to know a STR or INTEGER gadget's current value use getstr() or getinteger(). Topaz Fallback -------------- the function `easygui_fallbackA()' is equivalent to `easyguiA()' apart from the fact that when EasyGUI fails with the "bigg" exception, it will try again with topaz-8. Note that you should never want to rely on topaz, this function was only added for emergency situations. If your GUI is too big on some systems, you should redesign your GUI to fit comfortably instead. (see other parts of this doc that talk about GUI-size and testing). GUI Manipulation ---------------- closewin(gh) Temporarily closes the GUI window. Use openwin() to reinstate the GUI. This function is useful for iconifying a GUI (see the iconify and toolify PLUGINs). You can test whether a GUI's window is closed (or otherwise unmodifiable) by checking if the `wnd' element of the guihandle is NIL. Note that if your GUI is on the default public screen then it will no longer be a visitor (i.e., you can then use changescreen() to move it to a new screen). (A blocked window is unblocked before it is closed.) openwin(gh) Reopens a closed GUI window: it will reopen in the same position and size as it was when it closed. Note that the window is not guaranteed to open correctly again (someone may have grabbed your memory, for example). The window will always open unblocked. Once open again, gh.wnd will be non-NIL (i.e., the address of the window). changegui(gh,gui) Changes the GUI in a window (fairly) seamlessly. The old GUI is removed and the new GUI tries to take its place, resizing if necessary (a NIL `gui' is just ignored). The window will be resized if the new GUI's minimum size is bigger than the current size, or if the new GUI does not allow resizing. A side-effect of this new, dynamic ability to change GUIs is that the default window type is to have a size gadget, even the GUI can't be resized. You can change this by specifying a different EG_WTYPE, or use changewindowtype(). changemenus(gh,newmenus=NIL) Changes the GUI's menus. If `newmenus' is NIL or not present then the current menus are removed and the window is left with no menus. changeinfo(gh,info=-1) Changes the GUI's info. If `info' is -1 or not present then the GUI's info is changed to be the guihandle for the window. (Remember: the info is passed to the GUI's action functions.) changetitle(gh,windowtitle=NIL) Changes the title of the GUI's window. (But remember that the title is ignored for window type WTYPE_BASIC and WTYPE_NOBORDER.) changescreen(gh,scr=NIL) If the GUI window is closed (gh.wnd=NIL) then this will alter the GUI's screen (if it's open then this function does nothing). If `scr' is NIL then the default public screen will be used. If the font was linked to the screen then it will be updated, too. changefont(gh,tattr=NIL) Again, this works only if the GUI window is closed. If `tattr' is NIL then the font of the screen is used. changewintype(gh,wintype=WTYPE_SIZE) Another one that works only if the GUI window is closed. This sets the window border type. You can use one of WTYPE_NOBORDER, WTYPE_BASIC, WTYPE_NOSIZE and WTYPE_SIZE, as described above. movewin(gh,x=-1,y=-1) Moves the window of the GUI referenced by gh to the new (top-left) coordinate. If x or y is -1 (the default) then that coordinate is not changed. (Note: the change is delayed until the next INTUITICK.) sizewin(gh,xs=-1,ys=-1) Attempts to resize the window of the GUI referenced by gh. If xs or ys is -1 (the default) then that dimension is not changed. The resizing is not guaranteed to be carried out since the size may be smaller than the GUI's minimum or the GUI may not be sizable. (Note: the change is delayed until the next INTUITICK.) Simple window blocking to prevent the user using a GUI: blockwin(gh) This puts up an invisible requester over the window of the GUI referenced by gh, and disables window sizing. If you're running v39+ then the busy pointer will be set on this window. You would use this to block a GUI before you put up another one. Note: the window can still be moved and depth arranged. (This works only if the GUI's window is open.) unblockwin(gh) Reinstates the GUI, allowing the user to interact with it again. The blockwin() and unblockwin() calls nest, so you need an equal number of unblockwin() calls to blockwin() calls before the window is actually unblocked. Note: a GUI window is implicitly unblocked (forcefully!) when it is closed. In general: try to design your GUIs such that the user can use them all simultaneously, this is much friendlier (i.e., make use of the multi-window support given by addmultiA()...). checkgui(gh) Checks for any GUI messages and processes them, invoking actionfunctions if necessary. If this causes the GUI to want to be closed (e.g., the user has pressed the close gadget), then a "QUIT" exception is thrown and the exceptioninfo is the value that would have been returned by the easyguiA() call (so, as with quitgui(), your exception handlers might like to make "QUIT" a special case and just ReThrow() it). This function is useful, for example, when you're in an action function that's performing a long calculation. By polling the GUI at convenient points using this function you will make the GUI more responsive. However, you are responsible for coping with the possibility that the user may cause, for instance, the same action function to be called again. A simple solution is use some kind of semaphore (a global boolean variable, for example) to mark whether you are currently in an action function and busy. See the led_test.e examples and the old CU Amiga tutorials on Amiga E. checkmulti(mh) This is checkgui() for multi-window GUIs. As mentioned above (in 'Multiple Windows') you can use checkgui() to check and act on messages for *all* the GUIs in the same group as the one identified by the guihandle, but the guihandle must be valid. This function uses the multihandle so is always safe as long as the group exists. +---------------------------------------------------------------+ | 5. PLUGINs | +---------------------------------------------------------------+ PLUGINs allow the programmer to extend EasyGUI with new functionality, i.e., to add any kind of rendering/gadgets to the GUI, while cooperating automatically with EasyGUI's layout/resizing. You can use plugins to add rendering areas in the midst of EasyGUI gadgetry (e.g., for graphics programs), add BOOPSI gadgets to a GUIs etc. You can supply ready-made plugins for other programmers to use. OBJECT plugin /* Public (read-only) parts: */ -> Position and size. x:INT,y:INT,xs:INT,ys:INT -> guihandle for the GUI the PLUGIN is on. gh:PTR TO guihandle ENDOBJECT Any PLUGIN is an object inherited from the 'plugin' object found in easygui.m. To implement a new plugin all that needs to be done is redefine a few methods. Then, this PLUGIN can be plugged in to any EasyGUI with for example: [PLUGIN,{plugaction},NEW mp.myplugin()] in your GUI-spec. Whether the action-value is used depends on the PLUGIN, as we'll see below. If it is called it will be passed the PLUGIN object as well as the GUI info: PROC pluginaction(info,plugin_object:PTR TO plugin) Have a quick look at the plugin example sources, or keep them handy while reading the bit below. Creating the object ------------------- create your new object as a 'plugin'-subtype. You may add constructors/ destructors if you wish. following methods implement the plugins behaviour, and may be redefined: will_resize() is called once just before the window is opened. You should return a flag-set telling in which ways your object can resize, making use of the constants RESIZEX and RESIZEY, 0 of course meaning your object is fixed in size (default method returns RESIZEX OR RESIZEY, i.e., resize in both directions). New to v3.3a is COND_RESIZEX and COND_RESIZEY, which allow your PLUGIN to resize in the corresponding direction only if something else in its gadget group is unconditionally resizable. (For example, a BAR is conditionally resizable, so that it does not force a group of BUTTONs to be resizable.) min_size(ta:PTR TO textattr,fontheight) is called once just before the window is opened. ta is the font used in the GUI. you should return the _minimum_ x and y sizes of your PLUGIN as _two_ returnvalues, making use of `fontheight' if you wish. Note that EasyGUI may actually grant you a lot more space than just the minimum space you ask for, depending on the other gadgets and user-resizing. If your object can have al sorts of sizes, pick a relatively small one as minimum. The default method just returns (fontheight,fontheight) render(ta:PTR TO textattr,x,y,xs,ys,win:PTR TO window) Here you should render your object to the window. In the case of a gadget this means creating the gadget and attaching it to the window (AddGList(), RefreshGList(), make sure you render only your own gadget(s)). ta is the font used in the GUI. (xs,ys) is always at least the minimum size you asked for. (x,y,xs,ys) is also copied to your object before this method is called, for use in other methods). The default method paints a nice black box :-) You shouldn't take too long in this method as it will make window redraw look slow. (It is safe to use existing v3.2e PLUGIN modules, but to recompile under v3.3a you will need to add the `ta' argument to your PLUGIN render() method.) gtrender(gl,vis,ta:PTR TO textattr,x,y,xs,ys,win:PTR TO window) New to v3.3a: a replacement for render() which enables simple addition of GadTools gadgets to the standard EasyGUI gadgets. If you want to use this method instead of the normal render() then: 1) the PLUGIN must support it, and 2) you must specify TRUE for the `isgt' field of the PLUGIN gadget. gtrender() should simply create its gadgets by linking into the current list (given by `gl') and using the visual info `vis'. It should then return the new gadget list (i.e., the last GadTools gadget linked in). And then that's it: your GadTools gadgets will be freed automatically, so if your PLUGIN is just GadTools gadgets you don't need to override the normal (empty) definition clear_render(). If you use gtrender() then you need not override render(): this will help indicate a problem if users forget to specify `isgt' with your PLUGIN. See the password PLUGIN example. clear_render(win:PTR TO window) mainly useful for gadgets to remove the gadget from the window and free it (RemoveGList(), remove only your own gadget(s)!). Normally render() is called when the window opens, and clear_render() when the window closes, when the user resizes, however, clear_render() and render() are called one after another, in that order, to account for the changed window layout. The default method does nothing. message_test(imsg:PTR TO intuimessage,win:PTR TO window) is phase-1 of the message handling, splitted in two to not block intuition too much. In message_test() the only thing you should do is return TRUE _if_ and _only_if_ the intuimessage is meant for your object, otherwise FALSE. (The only exceptions to this are IDCMP_MOUSEMOVE messages from a GACT_FOLLOWMOUSE or WFLG_REPORTMOUSE, which can be ignored safely [i.e., you may return FALSE] as they cannot easily be attributed to an originating gadget). For a mouse-click, test if it was in your area (the current dimensions of your object in the GUI are present in the `plugin' object), for gadgets, make sure it is really your gadget causing the message (check .iaddress). If you reply TRUE to messages that are potentially meant for other objects, you might choke them. Do not engage in other actions in this method, such as rendering; do these things in the message_action() method. The default method returns FALSE. The GUI window uses the following IDCMP flags: IDCMP_ACTIVEWINDOW IDCMP_INACTIVEWINDOW IDCMP_GADGETDOWN IDCMP_GADGETUP IDCMP_INTUITICKS IDCMP_MOUSEBUTTONS IDCMP_MOUSEMOVE IDCMP_NEWSIZE IDCMP_RAWKEY IDCMP_VANILLAKEY You may get your PLUGIN objects to generate these messages (via gadgets) and then trap them (via message_test()), or you can just snoop on them in message_test(). message_action(class,qual,code,win:PTR TO window) is the second part, and will only be called if you returned TRUE in the previous method. here you may do any action needed (additional rendering based on the user action). class, qual and code are copies of the class, qualifier and code part of the intuimessage, so you don't need to remember them in message_test(). You should return TRUE if you want the action the user of the PLUGIN has written in his GUI-spec to be executed upon termination of this method (do this if your object clearly can be "hit", such as a gadget. If you just do some rendering you might want to ignore the actionvalue). The default method returns FALSE. (As noted above, the action function is passed the PLUGIN object as well as the GUI info.) Note: do not confuse message_action() with action values: the former is for implementing the plugin's behaviour generally, while the latter is for specific behaviour in a specific GUI, attached by the user of your plugin (which could be you again :-). appmessage(amsg:PTR TO appmessage,win:PTR TO window) Like the testing for window IDCMP, but for AppWindow message testing. Return TRUE _only_if_ the message is meant for this PLUGIN. The PLUGINs (optional) awproc will then be called (as normal, with the optional data field being the `plugin_object'). Note: for compatibility, the appmessage() method is called only for PLUGINs that specify a non-NIL `awproc' field. Just be careful to *not* specify a non-NIL `awproc' field with old PLUGIN modules. If you supply a plugin as module for others, you'll have to state what constructor(s) they may use, wether or not they have to supply a sensible action-value. END will almost always have to be called. +---------------------------------------------------------------+ | 6. guide to the examples | +---------------------------------------------------------------+ There are numerous examples to show the various features of EasyGUI. Here's a small guide to help you find the ones that might be interesting: alldist.e - The original example. alldist2.e - The original example, using multi-windows and blocking. You can have all the example GUIs open at once! testaw.e - AppWindow example. testchange.e - changegui() example. testchange2.e - changeXXX() and multi-window example. testkey.e - Keyboard shortcuts example, with window blocking and gadget disabling. testmulti.e - Multi-window example (recursive!). testmulti2.e - Multi-window and multiforall() example. testqual.e - Qualifier example. animcontrol_test.e - Tests animcontrol PLUGIN. button_test.e - Tests flavours of button PLUGIN. calendar_test.e - Tests calender PLUGIN. colorwheel_test.e - Tests colorwheel PLUGIN. gradient_test.e - Tests gradient (slider) PLUGIN. imagebutton_test.e - Tests flavours of imagebutton PLUGIN. led_test.e - Tests led PLUGIN, with pseudo-async activity. led_test2.e - Tests led PLUGIN, with multi-windows and pseudo-async activities. led_test3.e - Tests led PLUGIN, with multi-windows and *real* async activities. led_test4.e - Tests led and ticker PLUGINs, with multi-windows and pseudo-async activities. password_test.e - Tests password PLUGIN. tabs_test.e - Tests tabs PLUGIN. tabs_test2.e - Tests tabs PLUGIN and changegui() example. tapedeck_test.e - Tests tapedeck PLUGIN. ticker_test.e - Tests ticker PLUGIN. These can be found in Src/EasyGUI. The sources for the example PLUGINs are in Src/Plugins. +---------------------------------------------------------------+ | 7. bugs/future | +---------------------------------------------------------------+ bugs: - method of displaying slider values not bulletproof - GadTools sends two messages if your slider value is changed to a negative value, so your action function can get called twice. [the planned render spaces can now be done much better by using PLUGINs] ---------------------------------------------------------------- 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 (and Jason!)