This FAQ is currently just a collection of responses to recent questions from Amulet users that may have some general interest.
Back to the Amulet Home Page.
AMULET is an entirely free, public domain user interface development environment in C++. Code written using Amulet will work with little or no changes on all platforms.
Now at Version 3.0, Amulet is more than just another free "virtual toolkit." Amulet includes many features specifically designed to make the creation of highly-interactive, graphical, direct manipulation user interfaces significantly easier. Important features of Amulet include: high-level support for interactive behaviors; built-in support for animation, UNDO, and gesture-recognition; a dynamic, prototype-instance object system that makes prototyping easier; automatic constraint solving integrated with the object system; a "structured-graphics" model (also called a "display list") that handles automatic refresh of objects when they change; a high-level input model that makes it easy to add behaviors to objects and also supports undo and help for operations; a full set of flexible widgets implemented using the Amulet intrinsics, so you can easily experiment with your own widgets; and high-level interactive debugging tools. Widgets include: buttons, check boxes, radio buttons, menus, menu bars (pull-down menus), scroll bars, scrolling windows, and text input fields. Amulet comes with complete documentation including a tutorial.
Amulet has been downloaded over 10,000 times, and about 80 projects are listed in the Amulet Users web site. Amulet is listed in the C++ FAQ and sites about tools for Linux. Amulet is being developed by the User Interface Software Group in the Human Computer Interaction Institute in the School of Computer Science at Carnegie Mellon University. The primary research interest of the UISG is the development of tools to allow rapid development of graphical user interfaces.
Amulet is available for downloading from the Amulet web site.
http://www.cs.cmu.edu/~amulet/amulet3-release.html
Amulet is available for free by anonymous FTP or WWW. Amulet has been put into the public domain. This means that anyone can use Amulet for whatever they want. In particular, Amulet can be used for commercial development without any license or fees. The resulting binaries and libraries can also be distributed commercially or for free without payments or licenses to Carnegie Mellon University (CMU). You can even include portions of the Amulet source code in your projects or products. The only restriction is that the documentation for Amulet is copyrighted, so you cannot distribute the Amulet manual or papers without permission from CMU. In return, CMU assumes no responsibility for how well Amulet works, and will not guarantee to provide any support. If you need more formal legal language, see Section 1.12 of the manual. If you need this formally signed, then replace your company's name for COMPANY and send it back to us.
Of course, the Amulet research group would appreciate any corporate grants
or donations to support the further development and maintenance of Amulet.
We would also be interested in discussing grants to support adding specific
features to the system that would make it more useful for your needs. Please
contact Brad Myers at
bam@cs.cmu.edu
to discuss
this.
If you decide to use Amulet, we would like to be able to mention this in
our publicity and reports to our sponsors. (We get recognition for having
users, both commercial and research projects.) Please send mail to
amulet@cs.cmu.edu
with
the name of your project or product. We also like to receive screenshots.
If you write papers or advertisements about systems built with Amulet, we
would appreciate if you included a mention that you used Amulet, and a reference
to this manual, and we would like a copy of your paper for our files.
For complete license and legal information, please see Section 1.12 of the manual.
"I notice the presence of the gpl COPYING disclosure in the src/gesture directory. Does this mean that I can't use amulet in my commercial project without adhering to the gpl constraints of providing my customers and competitors with my source code for free?"
Some of the gesture code was copied from Dean Rubine's code from his PhD thesis, and he put the GPL warning in his code. If you want to be really legal, you should probably not use the gesture recognition part of Amulet in commercial software without adhering to the GPL restrictions. We have evaluated the gesture code and feel that we could rewrite the necessary parts so we would not need to include the GPL copying notice. If someone would like to make a grant to the Amulet project to support this rewriting, we might be willing to do this; send mail to amulet@cs.cmu.edu. Then the entire Amulet system would be in the public domain.
Internet Explorer can be confused by the file name
amulet.tar.gz
. It renames the file to be more DOS-like as the
file is brought across the Internet, amulet_tar.gz
. To
get WinZip to decompress the file correctly, simply rename the downloaded
file back to amulet.tar.gz
and run WinZip on the file.
For subscribing and unsubscribing to the amulet-users list send email to: amulet-users-request@cs.cmu.edu
2.2 To contact the Amulet group:
Send mail to one of the amulet email addresses with problem reports and questions, to bring issues to the attention of the appropriate group within the project.
The amulet email addresses include:
Link to the "Index of Messages on the Amulet-Users mailing list" from the Amulet home page. From the index, you can search for topics of interest.
You can go directly to the index using the URL
http://www.cs.cmu.edu/~amulet/hypermail/index.html
These are the compilers and platforms the Amulet developers have personally tested.
Operating Systems | Compiler(s) |
SunOS | gnu C++ 2.7.2; Centerline's Object Center 2.1 |
HP/UX | gnu C++ 2.7.2; Centerline's Object Center 2.1 |
MacOS 7.6 and MacOS 8 | CodeWarrior 10; CodeWarrior Pro 1 |
Windows 95 | Microsoft Visual C++ V4.2; Microsoft Visual C++ V 5.0 |
Windows NT 4.0 | Microsoft Visual C++ V4.2; Microsoft Visual C++ V 5.0 |
Amulet shared libraries not supported on gcc versions before 2.7.0.
Others may be contributed and supported by users from time to time, but generally you will need to make certain changes to port to any other platforms. See the contributions page for details.
CodeWarrior 10 uses the older standard libraries, CodeWarrior Pro 1 requires MSL Standard C++ Libraries.
Users have made substantial contributions to Amulet in monetary contracts, suggestions and code. These include bug fixes, bug reports, and contributions of ported source code and make files. Here is a partial list of contributed code that may be useful in your projects.
From time to time users port Amulet to other platforms and compilers. Please send complete files that contain updates/fixes to amulet@cs.cmu.edu Here is a list of contributed makefiles:
Many Amulet users have created useful software that they are willing to contribute for the use of other people. These contributions are available can be found on the contributions page.
The software is not supported by the Amulet group. Any contribution may carry certain copyrights that are different from Amulet. Please consult the file or the author before using in commercial code. These have not been tested by the Amulet staff. They may not even work on your particular platform.
Generally, Amulet and other toolkits do not coexist well together. This includes MFC, which does not work with Amulet. The issue is that each framework likes to control the event loop and control the window drawing.
Is there any software trick to decrease this linking time? If a small test sample takes 10 min. to link...What about a complete application ?
Its not Amulet's fault, it happens for all large apps. VC++ 4.2x did not have this problem.
VC++ 5.0 takes ages to link in release mode. The service pack (70MB!!) helps fix this problem. That's it. Compilation time deacreased drastically from 10 min. to about 30 sec! Here is the information:
http://www.microsoft.com/kb/articles/q168/9/12.htm
http://www.microsoft.com/kb/articles/Q151/5/01.htm
The User Interface Software Group first created a tool called Garnet in Lisp in 1988, and then in Summer of 1994 started working on Amulet. It took about a calendar year, and over 4 man-years of work to create Amulet, without much research contributions. Therefore, we are reticient to start yet another conversion. Java and C++ are enough different that we feel we would want to do a redesign and not just a transliteration.
However, we feel that Java would definitely benefit from many of the innovations in Amulet, including expecially the command object architecture (undo), constraints, input handling (interactors), and animation. We would certainly be interested in talking with anyone who wanted to fund the conversion with a grant, or do the conversion with our help!
The Amulet project is currently funded mostly by a government grant, and we would certainly appreciate more industry support. Please contact bam@cs.cmu.edu. In exchange for a grant, we may be able to add new features that are important to your company, or possibly adjust the priorities of our enhancements to match your needs.
If we do not receive grants from users, support for Amulet will terminate soon. See the note about Amulet Support.
It is also useful to the Amulet project to have comments for the comments page, pictures of your applications for our pictures page, and a large list of users for our users page. This helps with our presentations to demonstrate that Amulet is being widely used, for many different kinds of applications.
If you have created some software or widgets that you think other Amulet users might benefit from, we have a contributions page of user-contributed software. Please send the information about your software to amulet@cs.cmu.edu.
One user has reported successfully using with OpenGL. If you are interested in contributing code that adds OpenGL support to Amulet, wewould be most happy to make it available to other Amulet users.
Apparently text data isn't sent to the input fields under OpenWindows unless
you set a certain parameter. This should get around the problem. Add the
following line to your .Xdefaults
file:
OpenWindows.FocusLenience: True
You can obtain contributions from the contributions web page. Please be sure to read the disclaimers at the top of the page before downloading.
Sometimes users will encounter an error under Unix that is long and involved but includes a statement like this:
ld: cannot open -lX11: No such file or directory
The compiler cannot file the Xlib libraries or include files.
In short, you may need to include the pathnames for you Xlib files in the
AM_CFLAGS
and the AM_LIBS
lists in
Makefile.vars.custom
the include path should be provided
with the -I
switch, and the library path should be provided
with the -L
switch. For more details see p 39 of the V3 manual
which includes an example of what you need to do.
This error typically appears when your make utility doesn't understand the include directive. You may want to try using another version of make.
Alternatively, you may be using the wrong version of the Makefile. Did you download the Unix version of Amulet, or the Windows 95/NT version? The Windows 95/NT version has a slightly different makefile, which uses "!include" as the include directive. You need the Unix version, which uses "include" as the include directive.
I get a list of errors that begins like and then goes on for a couple pages:
E:\amulet/include\amulet/types.h(32) : error C2371: 'bool' : redefinition;
different basic types
E:\amulet/src/opal/opal.cc(960) : error C2440: 'type cast' : cannot
convert from 'const class Am_Value' to 'bool' Ambiguous
user-defined-conversion
If you are using Microsoft Visual C++ 5.0, you should set your
AMULET-VARS_FILE
to Makefile.vars.MSVC5.win32
.
It omits the compiler switch -DNEED_BOOL
that is required by
previous versions of the Microsoft compiler.
You can obtain a porting document from http://www.cs.cmu.edu/~amulet/CWPro2Port.txt
Date: Fri, 13 Nov 1998 13:17:54 -0500
To: amulet-users@cs.cmu.edu
From: David Kieras <kieras@eecs.umich.edu>
Subject: Re: Amulet and CodeWarrior3
I thought this might be useful for the group ...
I am now using CWP4, which version is even closer to standard in important ways, so I recommend it. But I haven't yet had a chance to get back to trying to bring up amulet under CWP4 - I'm planning to do that soon!
Please let me know more specifically what you are seeing, and I'll dredge my archives and see what I have that might be useful to you.
-- David Kieras, EECS Dept., Univ. of Michigan
Answer from Dean Edmonds <deane@gooroos.com>:
1) If you don't have GNU make installed (i.e. you are using SGI's standard make utility) then you will have to edit amulet/bin/Makefile and put a `-' immediately before each of the $(RM) commands. For example, the following:
develop-shared: $(RM) $(ANY_OBJECT_FILE) $(MAKE) $(AS_SHARED) $(AS_DEVELOP)would become:
develop-shared: -$(RM) $(ANY_OBJECT_FILE) $(MAKE) $(AS_SHARED) $(AS_DEVELOP)This is necessary because SGI's make dies whenever a file expansion (e.g. *.o) doesn't match any files.
2) Make the static version of the library. E.g:
make develop-static3) cd to the lib directory and create the shared version of the library by hand. E.g:
cd ../lib CC -w -shared -all libamulet.a -o libamulet.so
Answer from Jay Gowdy <Jay_Gowdy@bagpipe.msl.ri.cmu.edu>:
I just ignore this, and it seems to have no practical impact. Does anyone really know what it means?
Answer from Jay Gowdy <Jay_Gowdy@bagpipe.msl.ri.cmu.edu>:
Believe it or not, this is actually due to a relatively obscure bug in the old SGI C++ compiler (-o32). You can either change the definition of Am_Beep in src/opal/opal.cc to Am_Beep(const Am_Object& window=Am_No_object) or you can try to get amulet to compile with -n32. This takes a fair amount of work (including some source code changes to get over the fact the the newer MIPSpro SGI compilers are really insistent that bools are _not_ ints). I submitted those changes a while ago, but I can't remember if they made it into V3.0 or not (or if they are just in my copy).
Answer from Michiel Ephraim <michiel.ephraim@research.techforce.nl>:
The definition of NULL was changed from gcc-2.7.x to gcc-2.8.x.
The quick and dirty solution is to replace "NULL" with "0" on the offending lines. Someone else on this list suggested "Am_No_Object" instead of "0".
See also the V3 manual on page 43.
All exported symbols start with Am_ except for C++ methods and C++ instance variables of objects (e.g, Am_Button, but obj.Create() ) since Create is a method).
All exported symbols use full words spelled out (e.g.: Command instead of Cmd)
All multi-word symbols used underscore between the words (e.g. Am_Menu_Line_Command).
The first letter of each word is capitalized.
All constants are in ALL CAPS (except for the preceeding Am_). Slot names are constants, so they are in all caps: Am_LEFT.
See the V3 manual on page 94.
#include <amulet.h>
// create an object named "line5"
Am_Object new_line = Am_Line.Create("line5")
// retrieve the object named "line5"
Am_Value val = Am_Value::From_String("line5");Am_Object obj = val;
Note that many different types can have a name, so the From_String call returns an Am_Value of the appropriate type. To be more careful, you might check to see that the value is Valid and that it is an object:
if (val.Valid() && val.type == Am_OBJECT)
obj = v;
Also, be aware that this only works when Amulet is compiled for debugging (-DDEBUG). When Amulet is compiled with debugging turned off, the names of Amulet objects are just thrown away.
Here is some additional information about Amulet object names:
The object names:
o1 = Am_Rectangle.Create("R"); o2 = Am_Rectangle.Create("R");
The Amulet manual documents how to set environment variables under Windows NT. Under Windows 95, you need to set environment variables in the file AUTOEXEC.BAT, which you should find at the top level on your C: drive. The syntax is "set var=value", so if you wanted to set AMULET_DIR to C:\AMULET you would write:
SET AMULET_DIR=C:\AMULET
Amulet methods use a pointer to the C++ function in order to store the method
in a slot of the Amulet object. As discussed in the standard C++ FAQs,
you cannot generate a pointer to a C++ member function since it wouldn't
know which C++ object the method should be associated with. Instead,
you should store the C++ object into a slot of the object, (see
FAQ item 5.2 about how to do this) and then
write a new Amulet method (using Am_Define_Method
) that calls
the desired member function of that object.
There is a discussion of this in the V3 manual on page 39. Much of the size is taken up with debugging information, some generated by the compiler and some by Amulet. Programs will be much smaller, and run somewhat faster, by recompiling the Amulet libraries will all debugging turned off. The executable for a small application compiled with full optimizations and debugging off on the PC will be about 500K. On Unix using gdb, you can also use shared libraries to make the size of the executable files be small.
Regular Amulet wrappers are specifically designed to NOT share the same C++ object among different Amulet objects. In particular, wrappers do not keep back-pointers to all the objects that use them. Therefore, when a wrapped C++ object is changed, it is not able to notify all the Amulet.
However, this is not true of pointer_wrappers or objects stored into
slots using Am_Ptr casts. In this case, all objects will share a pointer
to the same C++ object. However, in this case, Amulet will not
know when the pointed to C++ object changes, so the programmer should be
sure to call Note_Changed
on the Amulet object whenever the
C++ object changes.
Alternatively, instead of referencing the same C++ object from multiple
places, put the C++ object into one slot of one Amulet object (maybe a special
off-screen Amulet object) and use constraints to reference that slot
whereever else the value is needed. Then as long as the one main slot is
notified (by Note_Changed
or a Set
on that slot)
when the C++ object changes, then all the other uses will be automatically
notified by the constraint system.
Amulet users will often run certain tools to help them debug their code and find memory leaks. When they do, Amulet reports what appear to be leaks. In fact, we shouldn't call them "memory leaks", because they don't cause memory exhaustion at runtime -- they're just not cleaned up at exit.
We made a valiant effort to remove all the causes of the memory leak warnings in V3, but discovered it was just too difficult. A number of global variables are initialized with data structures that do not get much bigger after initialization (so they shouldn't cause much problems for running programs), and we could not find a good way of cleaning them up on exit. We spent over a man-month on this issue and decided it was not worth any more time. If someone would like to make a grant or donation to the project to support this effort, we can spend the time to eliminate the rest of the leaks.
Sometimes Opal might not be fast enough. In these cases, you might want to create your own new, custom object. NOTE: This is not particularly recommended, and should only be attempted by expert Amulet users.
Make a single Amulet object as a subclass (instance) of Am_Group that is the size of all the custom object, and then override the Draw method of the new object
All Amulet objects need to have their size specified (left, top, width, height) and it is important that all object drawn are clipped to that bounding box.
Creating the new custom object as an instance (like a subclass) of one of the built-in graphical objects will allow it to inherit many of the standard methods. Am_Group seemed like the most appropriate for your application, but an instance of the fundamental Am_Graphical_Object might work just as well.
If you add new slots that control the drawing, you have to make sure Amulet knows that the object needs to be redrawn if these values change. This is done using the demons. For example:
Am_Object_Advanced temp = (Am_Object_Advanced&)my_object temp.Get_Slot (SLOT_THAT_CHANGES).Set_Demon_Bits (Am_MOVING_REDRAW | Am_EAGER_DEMON); temp.Get_Slot (ANOTHER_SLOT).Set_Demon_Bits (Am_STATIONARY_REDRAW | Am_EAGER_DEMON);
Am_MOVING_REDRAW is used for slots that when changed cause the object to be in a different place or size, and Am_STATIONARY_REDRAW is used for slots that do not cause the object to be a different size or place.
When using V3, you can put an Am_Menu
inside an
Am_Scrolling_Group
as below:
Am_Object list_in_box = Am_Menu.Create("list in the box")
.Set(Am_WIDTH, Am_From_Owner(Am_WIDTH))
.Set(Am_HEIGHT, Am_From_Owner(Am_HEIGHT))
.Set(Am_ITEMS, Am_Value_List()
.Add("First Item")
.Add("Second Item")
.Add("Third Item"))
;
Am_Object list_box = Am_Scrolling_Group.Create("a list box")
.Add_Part(list_in_box);
In the next version, there will be a built-in listbox widget.
V3 introduced the Am_Pointer_Wrapper
to make it easy to "wrap"
pointers to other data structures, to privide nice type checking and printing
of external data structures. See the V3 manual on page 112. For
example:
//In the .h file: class my_class { public: ... // whatever is required }; Am_Define_Pointer_Wrapper(my_class) // In the .cc file: class my_class_wrapper_support : public Am_Type_Support { public: void Print (ostream& os, const Am_Value& val) const { my_class * m = Am_my_class(val).value; os << print out my_class's data } } my_class_wrapper_support_obj; Am_Define_Pointer_Wrapper_Impl(my_class, &my_class_wrapper_support_obj); my_class *ii = new my_class; obj.Set(SLOT, Am_my_class(ii)); //wrap the pointer to my_class my_class *ii2 = Am_my_class(obj.Get(SLOT)).value;
If you don't want to use the Pointer_Wrapper support, you should cast your
pointers into the type Am_Ptr
. This works better than
(void*)
or (char*)
since Am_Ptr
s will
work on any platform.
CTest *pTest1 = new CTest, *pTest2;
Am_Value_List list;
list.Add( (Am_Ptr)pTest1);
list.Start();
pTest2 = (CTest *) (Am_Ptr) list.Get();
The (Am_Ptr
) cast is needed because the compiler doesn't know
how to convert a Am_Value
to random pointer, but if you tell
it to go through a (Am_Ptr
) pointer first, it will work fine.
In addition, Am_Ptr
correctly provide the correct cast
depending on the platform and compiler, since some compilers require
(void*)
and others (char*)
.
Since Amulet won't know anything about the pointers stored in the list, it
cannot delete the storage, therefore, you should iterate through the list
and delete all the objects explicitly before calling Make_Empty
or deleting the Am_Value_List
.
Actually the answer is documented in the V3 manual, look in the index under "Delete (on lists) method"! (page 107)
The Delete method destroys the item at the current position. It is an error to call Delete if there is no current element. The current pointer is shifted to the element previous to the deleted one.
Note that this applies to Am_Value_List
objects, not to other
iterators.
One word of caution -- if you delete the element at the start of the list, then the current element will positioned at the NULL list item between the Last and First elements, and Get will fail. You have to force the current element past the last by calling Next.
As explained in the V3 manual on page 259, the Am_ID
field will
be the return value of the widget if it is set into a command. Otherwise,
if the Am_ID
slot is 0 or not present, then the
Am_LABEL
of the command is returned instead.
If you use the Am_ID
slot, then you need to set the
Am_VALUE
of the widget to the ID value you want, rather than
the LABEL value you want (e.g., set it to be 1 instead of arrow_gif).
The errors look like this:
'typecast' cannot convert from 'Const class Am_value' to 'bool'
The issue has to do with compiler support for the new C++ keyword "bool". It turns out that this can be a major problem when porting to new systems. We have accomodated both compilers that support it and those that do not. What I see happening is there is a mismatch in the Amulet headers and your compiler, meaning that bool is supported
Check to see how your compiler turns on and off support for bool. Or you can find a way to "force" the header files to accept, add a line in amulet.h if you need to, either
#define NEED_BOOL 1
or
#undef NEED_BOOL
depending on whether your compiler supports bool or not.
That should make turn on or off the Am_Value to bool and other conversions.
If you are using Microsoft Visual C++ 5.0, you should set your
AMULET-VARS_FILE
to Makefile.vars.MSVC5.win32
.
It omits the compiler switch -DNEED_BOOL
that is required by
previous versions of the Microsoft compiler.
It is generally pretty easy to creat new widgets using the Opal level graphics primitives and the Interactors. Using Gem (as some of the "built-in" widgets do) is only for optimizations.
We do not use the native widgets on the platforms because it would be MORE difficult and less functional than re-implementing them. We would appreciate any contributions from the community of implementations of widgets we have not yet had time to do.
When C++ objects are stored as wrapper values in slots, Amulet cannot know when a program destructively modifies object behind its back. Slots must be notified explicitly in order to know that a wrapper value has changed.
The method Note_Changed
provides a means to notify a slot that
its value has changed through destructive modification.
Some_Wrapper wrapper; obj.Set (FOO, wrapper); wrapper.Destructively_Modify (); obj.Note_Changed (FOO);
The details are given in the V3 manual page 127 and includes an example that
shows how to let an Am_Value_List
know when a list item has
been changed.
Widgets interface to your application through command objects added as parts of the widgets. But to either get or set the Am_VALUE of the widget, you should access the Am_VALUE slot of the widget itself, not the command object. This is described on page 254 of the V3 manual.
widget.Set(Am_VALUE, 3);
You can also put a constraint into the Am_VALUE slot, if you want the value shown by the widget to track a slot of another object.
It is a common mistake to set the value in the Am_COMMAND object inside the widget.
See also
Setting the Am_VALUE
of the widget to the correct value will
make that button look pressed in. NOTE: if it doesn't seem
to be showing up, try setting the widget's
Am_FINAL_FEEDBACK_WANTED
slot to true (see the V3 manual on
page 262).
If you can't tell what the right value is, you can always press the button
as the user, then pop up the inspector on the widget and see what the value
of the Am_VALUE
slot is.
Note that the value you set must be the correct type. For example a radio
button must be a single value, a checkbox must be an
Am_Value_List
of values, and buttons are single values.
If you want multiple items to be selectable in an Am_Button_Panel, set the
Am_How_Set
slot of the widget and you can select multiple items
and then the Am_VALUE
slot must be a Am_Value_List
.
You might have a constraint in the line style menu's Am_VALUE
slot that depends on the value of the selection handle's value (which will
be the selected object). Be sure to make this constraint be
xxx.Multi_Constraint(true)
since the Am_VALUE
slot
will also be directly set when the user clicks on the menu.
OR, you can add a DO_METHOD
to the command in the selection
handles widget that imperitively updates the menu.
You can't put your procedure into the button. You have to put the method
into the Am_DO_METHOD
slot of the command object that is attached
to the button. Also, you have to use a method that is defined using
Am_Define_Method
, and not just a regular procedure. This
is shown on page 77 and the top of p 78 in the manual.
Amulet does not provide any way to draw on the background behind all windows (in the area where there aren't any windows). However, there is a neat feature where you can drag around a little window and use that window as the feedback object, so it will appear wherever the cursor is. The main disadvantage of this trick is that it is somewhat slow and the feedback must be a rectangle. However, it does work on all platforms. See the V3 documentation on page 216-217 (section 5.4.3). To see this feature compile testinter and move the objects around after pressing capital W.
I cannot set the "edge of roundtangle" to dash lines. It didn't work for the edge of rectangle, either. I only can create dash lines.
On the PC, the line styles using dashed don't work, they just look solid.
Maybe use Am_Red
or something instead of dashed.
This is very easy. You basically create an Am_Selection_Widget
object and add it to the group the objects are in. The best example
is in examples/example1.cc. There are several steps involved, but the example
is complete.
Line 250 shows how to add an instance of the standard selection widget, and on line 259 attaches it to the window group.
The move grow interactor lets me drag the mouse outside of a window, and then the Am_LEFT slot of the 'moved' object gets a negative value.
There are two ways to do this.
a) One way is to set the Am_RUNNING_WHERE_OBJECT
slot to a
containing graphical object. When the object is stretched outside the
containing graphical object, the interactor stops operating. The result
is that the object won't continue to grow past the boundary of the container.
If you want to refine the container further, set the
Am_RUNNING_WHERE_TEST
slot, which can check for the mouse movement
and respond as you intend. You can see this on page 216 of the v3 manual.
Or
b) The more common solution is to use a grid method. The grid will be used
hold the object inside the window or other rectangle regardless of where
the mouse goes. Information about this is on p 202 of the manual.
There is an example in examples/space.cc. When you run space, in the lower
right hand corner there is a box that slides adround that must stay within
space game's universe. In it, we set the Am_GRID_METHOD
slot
to a method called keep_inside_window
. keep_inside_window
at around line 745 shows how this is done.
First check the inspector for Am_LEFT, Am_TOP, Am_HEIGHT, Am_WIDTH values to see if they are reasonable. Then, if these are set through constraints, set break points in your code to make sure the formulas are being called and are responding as you expect. If you "flash" the object using the "Objects/Flash Object" command in the inspector, then this will often print a message to the console describing why the object isn't visible.
Also, remember that groups clip objects inside of them, so make sure that the groups that the objects are in are big enough.
The only image type we support directly is GIF. Other image support is unfortunately lacking. We do not support any other types including any of the platform specific imaging. We would certainly like to add other image types, if someone is interested in contributing to the Amulet project.
However, Amulet does support most of the functions that draw commonly found in platform toolkits. These include line, rectangle, and a whole set of shapes. If you are able to parse your particular image type, you can use the many Gem functions in Amulet to draw the picture. And of course, when you use the Gem functions your drawing code will be cross platform.
Sometimes the single click interactor might seem to steal the double clicks. The default start character for interactors is ANY_LEFT_DOWN which gets both single and double clicks. Therefore, the default interactors will operate on both single and double clicks. If you want an interactor to ONLY work on single click, then change its Am_START_WHEN slot to be "LEFT_DOWN" or equivalent, and then the double click interactor's Am_START_WHEN would be "DOUBLE_LEFT_DOWN" or equivalent. This is explained more completely on p 190-191 of the V3 manual.
Another way to do this is to make sure the double click interactor gets checked first. This can be insured by setting the priority of the double click interactor to be higher than the single click interactor. If you set the double click interactor priority to be greater than 101, double clicks will go to the interactor wanting double clicks. Priority levels are described on p 214-215 of the version 3 manual.
You could create an interactor and attach it to a window, and make it always running.
To get the interactor to be always running, see the code snippet on page
218 of the Amulet V3 manual (section 5.4.5). If you want to get to see every
mouse move event, you can set a method into the Am_INTERIM_DO
method of the command object in the interactor, see page 222 (section 5.5.2)
of the manual.
You might create a custom object with your own Draw method, and then put
this object on top of the rectangle. Amulet will call the draw method of
the object at the correct times. Objects can be moved to the top using
Am_To_Top
, and the draw method will be called whenever any of
the slots with "demons" attached change values. You might look at the code
of a widget, like the scroll widget (in src/widgets/scroll_widgets.cc) to
see how a custom object is created. We should probably create a simpler example
of how to make an object with a custom draw method.
First check the code to be sure your constraint is being called by putting
a breakpoint or print statements in your code. It may be that the
constraint is not being called as you expect. If that is correct, display
the object that the constraint is in using the Inspector. If the slot
doesn't have a constraint anymore, then probably the slot is being explicitly
Set, and this is removing the constraint. If you want the constraint
to stay even when the slot is set, then make the constraint be
.Multi_Constraint()
(see page 132 of the manual).
If that isn't the problem and the constraint is there, double-click on the constraint, and then pop up the constraint dependencies window using the menu command Windows/Show Constraint Dependencies. This window shows what the constraint depends on. Also, in the Inspector, you can select the slot the constraint is in and set a trace or break on the slot to see when the slot is changing value.
If you find the constraint is not being re-evaluated when it should be, it is because a dependency is missing. Sometimes a constraint is re-evaluated before the object is created, in which case no dependency is created. This will be evident in the Inspector.
You need to call Validate()
after setting each point of the
animation. This crucial observation was omitted from Section 6.9 of the Amulet
3.0 manual, where uninterruptible animations are discussed. The
Validate()
method ensures that the object system is consistent,
by running all pending demons and evaluating invalid constraints. (Calling
Get()
on any slot would have the same effect, by the way.) For
uninterruptible animations, calling Validate
(or
Get
) performs a crucial role: it delimits the points of the
animation. For example, this code creates a rectangle and animates it around
a square path. Notice that only one coordinate needs to be changed for each
point of the square:
Am_Object animator = Am_Animator.Create() .Set (Am_INTERRUPTIBLE, false); Am_Object moving_rect = Am_Rectangle.Create() .Set (Am_LEFT, Am_Animate_With (animator)) .Set (Am_TOP, Am_Animate_With (animator)); .Set (Am_TOP, 20) // animates from (0,0) to (0,20) .Validate (); .Set (Am_LEFT, 20) // animates from (0,20) to (20,20) .Validate (); .Set (Am_TOP, 0) // animates from (20,20) to (20,0) .Validate (); .Set (Am_LEFT, 0) // animates from (20,0) to (0,0) .Validate ();
Without the Validate
calls, this sequence of Sets
would be indistinguishable from an animation that jumps directly to (20,20)
and back to (0,0).
Since Get
has a similar effect to Validate
, you
need to be careful not to use Get
before you're done setting
a point of the animation. So this wouldn't work as desired:
// Don't do this moving_rect .Set (Am_LEFT, some_other_object.Get (Am_LEFT)) .Set (Am_TOP, some_other_object.Get (Am_TOP)) .Validate ();
Instead, make all your Get
calls before setting the point:
// This should work int l = some_other_object.Get (Am_LEFT); int t = some_other_object.Get (Am_TOP); moving_rect .Set (Am_LEFT, l) .Set (Am_TOP, t) .Validate ();
There are a number of common ways this happens:
xxxx.Add_Part(o1 = xxx.Create() ...) .Add_Part(o2 = o1.Create() //DOESN`T WORK
Another example is:
widget.Set(Am_ITEMS, Am_Value_List() .Add(c = Am_Command.Create() ...) ) .Set(Am_VALUE, c); //DOESN'T WORK
Instead, you must terminate the statement and start a new statement:
xxxx.Add_Part(o1 = xxx.Create() ...); xxxx.Add_Part(o2 = o1.Create() //fine widget.Set(Am_ITEMS, Am_Value_List() .Add(c = Am_Command.Create() ...) ); widget.Set(Am_VALUE, c); //fine
When I compile certain Am_Define_Formulas, I get warnings for each one of them that look like this:
clustervisr3.cc: In function `int right_of_printButton_proc(class Am_Object&)':
clustervisr3.cc:1409: warning: unused parameter `class Am_Object & self'
The reason you are getting the warnings is that we've turned on compiler
warnings by default. You can certainly go back and change the warnings level
in the command line. That's one "fix". Or you can fix the code and
instead of using Am_Define_Formula
, you can use
Am_Define_No_Self_Formula
. This is described on p101 of the
manual.
When you use Am_Define_No_Self_Formula
it comments out the unused
variable name in the macro so the compiler will know it isn't used inside
the formula.
Back to the Amulet Home Page.
Maintained by:
Bruce D. Kyle (last updated Jan 9, 1998)