This chapter describes simple graphical objects, styles, and fonts in Amulet. ``Opal'' stands for the Object Programming Aggregate Layer. Opal makes it easy to create and manipulate graphical objects. In particular, Opal automatically handles object redrawing when properties of objects are changed.
amulet.h
at the top of your files, but some programmers like to look at header files. Here is a list of most of the objects and other data types discussed in this chapter, along with the header file in which they are declared.gdefs.h
: Am_Style, Am_Font, Am_Cursor, Am_Point_List, Am_Image_Array
opal.h
: default Am_Style
's, default Am_Font
's, Am_Screen, Am_Graphical_Object, Am_Window, Am_Rectangle, Am_Roundtangle, Am_Line, Am_Arrow, Am_Polygon, Am_Arc, Am_Text, Am_Bitmap, Am_Group, Am_Map
, default constraints, Am_Initialize, Am_Cleanup, Am_Beep, Am_Move_Object, Am_To_Top, Am_To_Bottom, Am_Create_Screen, Am_Update, Am_Update_All, Am_Do_Events, Am_Main_Event_Loop, Am_Exit_Main_Event_Loop
, default Am_Point_In_
functions, Am_Translate_Coordinates, Am_Merge_Pathname
value_list.h: Am_Value_List
text_fns.h: all text editing functions, Am_Edit_Translation_Table
Opal is built on top of the Gem module, which is the Graphics and Events Module that refers to machine-specific functions. Gem provides an interface to both X windows and to Windows NT, so applications implemented with Opal objects and functions will run on either platform without modification. Gem is described in chapter 9.
Within the Amulet environment, Opal forms an intermediary layer. It uses facilities provided by the ORE object and constraint system, and provides graphical objects that can be combined to build more complicated gadgets. Opal does not handle any input from the keyboard or mouse -- that is handled by the separate Interactors module, which is described in chapter 5. The Amulet Widgets, such as scroll-bars and menus, are partially built out of Opal objects and partly by calling Gem directly (for efficiency). Widgets are generally more complicated than the Opal objects, usually consisting of interactors attached to graphics, and are discussed in chapter 7.
Each window defines a new coordinate system with (0,0) in the upper-left corner. The coordinate system uses the pixel as its unit of measurement. Amulet does not support other coordinate spaces, such as inches or centimeters, and it does not support transformations that take the display's aspect ratio into account. Amulet windows are discussed fully in Section 4.11.
A group is a special kind of object that holds a collection of other objects. Groups can hold any kind of graphic object including other groups, but an object can only be in one group at a time. Groups form a pure hierarchy. The objects that are in a group are called parts of that group, and the group is called the owner of each of the parts. Groups, like windows, clip their contents to the bounding box defined by their left, top, width, and height. Groups also define their own coordinate system, so that the positions of their parts are relative to the left and top of the group.
The graphical hierarchy in Amulet has Am_Screen
, the display device, at its root. Am_Screen's graphical parts are windows. Those windows can contain other windows, groups, or graphical objects as parts. No object is drawn unless Am_Screen
is one of its ancestors in the graphical hierarchy. It is important to remember to add all windows to Am_Screen
, or to another window on the screen, and to add all groups and objects to a window. If you do not, nothing will be displayed.
Non-graphical objects, like Interactors and Command objects (and application specific objects) can be added as parts to any kind of object, but graphical objects will only be displayed if they are added as parts to an Am_Window
or a Am_Group
(or an instance of these objects).
A traditional introductory program is called Hello World. This program displays the string ``Hello world'' in a window on the screen, and automatically refreshes the window if it is obscured. In Amulet, Hello World is a very short program:
#include <amulet.h>
main (void)
{
Am_Initialize ();
Am_Screen
.Add_Part (Am_Window.Create ("window")
.Set (Am_WIDTH, 200)
.Set (Am_HEIGHT, 50)
.Add_Part (Am_Text.Create ("string")
.Set (Am_TEXT, "Hello World!")));
Am_Main_Event_Loop ();
Am_Cleanup ();
}This code is provided in the Amulet source code in the file
samples/examples/hello.cc
. The same source code is used on all three platforms: Unix/X, Windows NT/'95, and Mac. Note that the Amulet code has a declarative programming feel to it, instead of standard imperative programming. Instead of giving Amulet step by step instructions on how to draw things, such as, ``first draw a window, then draw a string,'' and so on, you tell Amulet, ``Create a window. Create some text, and add it to the window. Go!'' The programmer never calls ``draw'' or ``erase'' methods on objects. Once you set up the graphical world how you want it, and ``Go'' (start the main event loop), Opal automatically draws and erases the right things at the right time.
Section 4.5 presents all the kinds of objects available in Opal.
Am_Initialize()
before referencing any Opal objects or classes. This function creates the Opal prototypes and sets up bookkeeping information in Amulet objects and classes. Similarly, a call to Am_Cleanup()
at the end of your program allows Amulet to destroy the prototypes and classes, explicitly releasing memory and graphical resources that might otherwise remain occupied after the program exits.
A call to Am_Main_Event_Loop()
should be the second-to-last instruction in your program, just before Am_Cleanup()
. Your program will continue to run until you tell it otherwise. All Amulet programs running Am_Main_Event_Loop()
can be aborted by pressing the escape sequence, which by default is META_SHIFT_F1
. Most programs will have a Quit option, in the form of a button or menu item, that calls the Am_Exit_Main_Event_Loop()
routine, which will cause the main event loop to terminate.
Am_Main_Event_Loop
) and perform the external or time-based actions yourself. This section explains how to write your own loop.
Normally, in response to an input event, applications will make some changes to graphics or their internal state and then return. Sometimes, however, an application might want to make a graphical change, have it seen by the user while waiting a little, and then make another change. This might be necessary to make something blink a few times, or for a short animation. To do this, an application must call Am_Do_Events()
to cause Amulet to update the screen based on all the changes that have happened so far.
You might also use Am_Do_Events()
to create your own event loop when Amulet's default event loop is not adequate. This might happen if you use Amulet with some other toolkit which also has its own main event loop. You might need to monitor non-Amulet queues, processes or inter-process-communication sockets. Calling Am_Do_Events()
repeatedly in a loop will cause all the Interactors and Amulet activities to operate correctly, because the standard Am_Main_Event_Loop
essentially does the same thing as calling Am_Do_Events()
in a loop. Am_Do_Events()
never blocks waiting for an event, but returns immediately whether there is anything to do or not. The return boolean from Am_Do_Events
tells whether the whole application should quit or not. A main-loop might look like:
main (void) {
Am_Initialize ();
... // do any necessary set up and object creation
// use the following instead of Am_Main_Event_Loop ();
bool continue_looping = true;
while (continue_looping) {
continue_looping = Am_Do_Events();
... // check other queues or whatever
}
Am_Cleanup ();
}
Am_LEFT
, Am_TOP
, Am_WIDTH
, and Am_HEIGHT
slots that determine their position and dimensions. Some objects have simple numerical values in these slots, and some have formulas that compute these values based on other properties of the object. Check the section below for a specific object to find its default values for these slots. All values must be int
s.
For all graphical objects, Opal never draws outside the bounding box specified by the Am_LEFT, Am_TOP, Am_WIDTH,
and Am_HEIGHT
of the object.
Am_VISIBLE
slot of all windows and graphical objects contains a bool
value which determines whether the object is visible or not. Objects that are not visible are not drawn on the screen. In a window, Am_VISIBLE
controls whether the window is drawn on the screen. To make a group and all of its parts invisible, it is sufficient to set the Am_VISIBLE
slot of the group to false
.Invisible objects are typically ignored by interactors and graphics routines. For example, you cannot use interactors to select an invisible object with the mouse, even if you click on the area where the invisible object would appear. Also, invisible parts of a group are generally not taken into account when the size of the group is computed.
Am_LINE_STYLE
and Am_FILL_STYLE
slots hold instances of the Am_Style
class. If an object has a style in its Am_LINE_STYLE
slot, it will have a border of that color. If it has a style in the Am_FILL_STYLE
slot, it will be filled with that color. Other properties such as line thickness and stipple patterns are determined by the styles in these slots.
Often you do not have to create customized instances of Am_Style
to change the color of an object. You can use a predefined style such as Am_Red
instead. Styles are fully documented in Section 4.7.
Am_HIT_THRESHOLD
, Am_PRETEND_TO_BE_LEAF
, and Am_VISIBLE
slots are used by functions which search for objects given a rectangular region or an (x, y) coordinate. For example, suppose a mouse click in a window should select an object from a group of objects. When the mouse is clicked, Amulet compares the location of the mouse click with the size and position of all the objects in the window to see which one was selected.
First of all, only visible objects can be selected this way. If an object's Am_VISIBLE
slot contains false
, it will not respond to events such as mouse clicks with conventional Interactors programming techniques.
The Am_HIT_THRESHOLD
slot controls the sensitivity of functions that decide whether an event (like a mouse click) occurred ``inside'' an object. If the Am_HIT_THRESHOLD
of an object is 3, then an event 3 pixels away from the object will still be interpreted as being ``inside'' the object. The default value of Am_HIT_THRESHOLD
for all Opal objects is 0. Note: it is often necessary to set the Am_HIT_THRESHOLD
slot of all groups that are owners of the target object; if an event occurs ``outside'' of a group, then the selection functions will not check the parts of the group.
When the value of a group's Am_PRETEND_TO_BE_LEAF
slot is true
, then the selection functions will treat that group as a leaf object (even though the group has parts). See Section 4.10.2 regarding the function Am_Point_In_Leaf
. Also, consult the Interactors chapter regarding the function Am_Inter_In_Leaf
.
Am_Rectangle
is a rectangular shaped object with a border of Am_LINE_STYLE
and filled with Am_FILL_STYLE
. The default rectangle is a 10 by 10 pixel square located at (0, 0), drawn with black line and fill styles.Am_Line
is a single line segment with endpoints (Am_X1
, Am_Y1) and
(Am_X2
, Am_Y2)
drawn in Am_LINE_STYLE (Am_FILL_STYLE
is ignored). Am_X1, Am_Y1, Am_X2, Am_Y2, Am_LEFT
, Am_TOP
, Am_WIDTH
, and Am_HEIGHT
are constrained in such a way that if any one of them changes, the rest will automatically be updated so the endpoints of the line and its bounding box are always consistent.Am_HEAD_WIDTH
and Am_HEAD_LENGTH
.Arcs are filled as pie pieces to the center of the oval when a colored filling style is provided.
Am_Roundtangle
prototype are rectangles with rounded corners.
The slots in this object are the same as Am_Rectangle
, with the additional slot Am_RADIUS
, which specifies the curvature of the corners. The value of the Am_RADIUS
slot can either be an integer, indicating an absolute pixel radius for the corners, or an element of the enumerated type Am_Radius_Flag
, indicating a small, medium, or large radius (see table below). The keyword values do not correspond directly to pixel values, but rather compute a pixel value as a fraction of the length of the shortest side of the bounding box.
Figure 4-6 shows the meanings of the slots of Am_Roundtangle
. If the value of Am_RADIUS
is 0, the roundtangle looks just like a rectangle. If the value of Am_RADIUS
is more than half the shortest side (which would mean there is not room to draw a corner of that size), then the corners are drawn as large as possible, as if the value of Am_RADIUS
were half the shortest side.
Am_Polygon
object is more complicated than other Opal objects. To specify the set of points that should be drawn for the polygon, you must first create an instance of Am_Point_List
with all your (x, y) coordinates, and then install the point list in the Am_POINT_LIST
slot of your Am_Polygon
object.
Section 4.6.6.1 lists all of the functions that are available for the Am_Point_List
class, including how to create point lists and add points to the list. Section 4.6.6.2 provides an example of how to create a polygon using the Am_Polygon
object and the Am_Point_List
class.
Conversely, whenever a new point list is installed in the Am_POINT_LIST
slot, the bounding box slots are recalculated. If you make a destructive modification to the point list class currently installed in the slot, such as adding a point to it with Add()
, then you must call Note_Changed()
on the polygon's Am_POINT_LIST
slot to reevaluate and redraw the polygon. (For more details about destructive modification of slot values and formula evaluation, see Section 3.14.1.)
The shape defined by an Am_Polygon
object does not have to be a closed polygon. To draw a closed polygon, make the first and last points in the Am_Point_List
the same. If they are not the same, and a fill style is provided, the border of the fill style will be along an invisible line between the first and last points.
Which sections of a self-intersecting polygon (such as a 5-pointed star) are displayed is controlled by the Am_Fill_Poly_Flag
part of the Am_FILL_STYLE
used for the polygon. See Section 4.7.3.6.
Am_Point_List
is a list of x-y coordinates, stored as pairs of floats
, in the coordinate system of the polygon's owner. It is implemented as a wrapper class with an interface very similar to the standard Am_Value_List
class (Section 3.9). Each Am_Point_List
object contains a current-element pointer which can be moved forward and backward in the list. The current point is used for retrieving, replacing, inserting, and deleting points in the list.
The Am_Point_List
is designed to be used to specify the vertices of a polygon object. Section 4.6.6.2 contains an example of how to use the Am_Point_List
class with the Am_Polygon
object.
These methods are constructors for Am_Point_List
. The complete definition is in gdefs.h.
Am_Point_List()
Return an empty list. (This constructor is implicitly called when you declare a variable of type Am_Point_List
.)
Am_Point_List(ar, size)
Return a list initialized from ar
, which is a flat array of values { x1 y1 x2 y2 ... xn yn }
, where size
is 2n
, and the points may be either ints
or floats.
Add(x, y)
Adds a new point (x, y) to the tail of the list. Returns a reference to the list, so multiple Add() calls can be chained together like ``list.Add(x, y).Add(x, y)...''
Add(x, y, Am_HEAD)
Adds a new point (x, y) to the head of the list. Returns a reference to the list.
Append(other_list)
Appends the points from another Am_Point_List
to the end of the list. Returns a reference to the list.
Start()
Makes the first point current. (Legal even if the list is empty.)
End()
Makes the last point current. (Legal even if the list is empty.)
Prev()
Makes the previous point current, wrapping around if current is first point of list.
Next()
Makes the next point current, wrapping around if current is last point of list.
First()
Returns true when current passes first point of list.
Last()
Returns true when current passes last point of list.
Get(int &x, int &y)
Sets x and y to the (rounded) x-y coordinates of current point. Error if no point is current.
Get(float &x, float &y)
Sets x and y to the x-y coordinates of current point. Error if no point is current.
Length()
Returns length of the list.
Empty()
Returns whether list is empty.
Set(x, y)
Sets the current point to (x, y). Error if no point is current.
Insert(x, y, Am_BEFORE)
Inserts (x, y) before the current point.
Insert(x, y, Am_AFTER)
Inserts (x, y) after the current point
Delete()
Deletes the current point.
Translate()
and Scale()
, if destructive modification is desired, pass false as an additional, final argument.Get_Extents(int &min_x, int &min_y, int &max_x, int &max_y)
Sets its arguments to the coordinates of the smallest rectangle (with integral coordinates) that completely encloses the points in the list.
Translate(offset_x, offset_y)
Transforms every point in the list by adding offset_x
to its x coordinate and offset_y
to its y coordinate.
Scale(scale_x, scale_y, origin_x, origin_y)
Transforms every point in the list by scaling its vector from (origin_x
, origin_y
) by scale_x
in the x direction and scale_y
in the y direction.
Am_Point_List
class are invoked using the standard C++ dot notation, as in ``my_point_list.Add (10, 20);
''.
Am_Point_List
, and then that point list should be set into the Am_POINT_LIST
slot of your Am_Polygon
object. The constructors for Am_Point_List
allow you to initialize your point list with some, all, or none of the points that you will eventually use. After the point list has been created, you can add and remove points from it.
Here is an example of a triangle generated by adding points to an empty point list. The point list is then installed in an Am_Polygon
object. To see the graphical result of this example, add the triangle_polygon
object to a window.
Am_Point_List triangle_pl;
triangle_pl
.Add (15, 50)
.Add (45, 10)
.Add (75, 50);
Am_Object triangle_polygon = Am_Polygon.Create ("triangle_polygon")
.Set (Am_POINT_LIST, triangle_pl)
.Set (Am_LINE_STYLE, Am_Line_2)
.Set (Am_FILL_STYLE, Am_Yellow);Here is an example of a five-sided star generated from an array of integers. To see the graphical result of this example, add the
star_polygon
object to a window.static int star_ar[12] = {100, 0, 41, 181, 195, 69, 5, 69, 159, 181,
100, 0};
Am_Point_List star_pl (star_ar, 12);
Am_Object star_polygon = Am_Polygon.Create ("star_polygon")
.Set (Am_POINT_LIST, star_pl)
.Set (Am_FILL_STYLE, Am_No_Style);
Am_Text
object is used to display a single line of text in a specified font. The Am_TEXT
slot holds the string to be displayed, and the Am_FONT
slot holds an instance of Am_Font
.
The Am_CURSOR_INDEX
slot determines where a text insertion cursor (a vertical line) will be drawn. The slot contains an integer specifying the position of the cursor, measured in number of characters from the start of the string. A value of zero places the cursor before the text, and a value of Am_NO_CURSOR
turns off the cursor.
The Am_WIDTH
and Am_HEIGHT
slots contain formulas that calculate the size of the object according to the string and the font being displayed. If the value of either of these slots is set, the formula will be removed, and the text object will no longer change size if the string or font is changed. In that case, a formula in the Am_X_OFFSET
slot scrolls the text left and right to make sure the cursor is always visible.
The Am_LINE_STYLE
slot controls the color of the text and the thickness of the cursor. If a style is provided in the Am_FILL_STYLE
slot, then the background behind the text will be filled with that color, otherwise the background is transparent. Setting Am_INVERT
to true
causes the line and filling styles to be switched, which is useful for ``highlighting'' the text object. If Am_INVERT
is true
but no fill style is provided, Amulet draws the text as white against a background of the line style color.
Am_Text
objects, for example:Am_Object my_string = Am_Text.Create()
.Set(Am_TEXT, ``This is my string'');However, Amulet converts these C++
char*
strings into Amulet's Am_String
type, which has the advantage that it is garbage collected. The main reason you need to know this is that with some compilers, when you Get
a string out of a slot, you may need to get it into an Am_String
first, and then convert it to a char*
. For example, char * s = my_string.Get(Am_TEXT); //MAY NOT WORK ON SOME COMPILERS
Am_String str = my_string.Get(Am_TEXT); // always works
char * s = str; //this will workThis two-step conversion seems to be especially necessary when using the regular C string utilities, for example:
Am_String str = my_string.Get(Am_TEXT);
char* s = str;
char* hasdot = strchr(s, `.');If you are going to set the value into another slot or into an
Am_Value
, you can leave it as the Am_String
.Am_Font
is a C++ class defined in gdefs.h
. Its creator functions are used to make customized instances describing the desired font. You can create fonts either with standard parameters that are more likely to be portable across different platforms, or by specifying the name of a specific font. The properties of fonts are: family (fixed, serif, or sans-serif), face (bold, italic, and/or underlined), and size. Am_Default_Font,
exported from opal.h
, is the fixed-width, medium-sized font you would get from calling the Am_Font
constructor with its default values. Allowed values of the standard parameters appear below.
In the creator functions for Am_Font
, the allowed values for the family parameter are (the Japanese, Chinese and Korean fonts do not currently work on the Macintosh.)
Am_FONT_FIXED
a fixed-width font, such as Courier.
Am_FONT_SERIF
a variable-width font with ``serifs'', such as Times.
Am_FONT_SANS_SERIF
a variable-width font with no serifs, such as Helvetica.
Am_FONT_JFIXED a
Japanese fixed-width font
Am_FONT_JPROPORTIONAL
a Japanese proportional font on Windows,Am_FONT_JFIXED
on X
Am_FONT_CFIXED
a Chinese fixed-width font on X,Am_FONT_JFIXED
on Windows
Am_FONT_KFIXED
a Korean fixed-width font on X,Am_FONT_JFIXED
on Windows
Am_FONT_SMALL
a small size: about 10 pixels tall
Am_FONT_MEDIUM
a normal size: about 12 pixels tall
Am_FONT_LARGE
a large size: about 18 pixels tall
Am_FONT_VERY_LARGE
a larger size: about 24 pixels tall
Am_FONT_TSMALL
a small size for Japanese, Chinese and Korean fonts
Am_FONT_TMEDIUM
medium size for Japanese, Chinese and Korean fonts
Am_FONT_TLARGE
large size for Japanese, Chinese and Korean fonts
Am_FONT_TVERY_LARGE
a very large size for Japanese, Chinese and Korean fonts.
//creators
Am_Font (const char* the_name); //create from a name, generally not portable
Am_Font (Am_Font_Family_Flag f = Am_FONT_FIXED,
bool is_bold = false,
bool is_italic = false,
bool is_underline = false,
Am_Font_Size_Flag s = Am_FONT_MEDIUM);
void Get(Am_String &name,
Am_Font_Family_Flag &f,
bool &is_bold,
bool &is_italic,
bool &is_underline,
Am_Font_Size_Flag &s);
bool operator== (const Am_Font& font) const;
bool operator!= (const Am_Font& font) const;
static bool Font_Name_Valid (const char* name);The following example of creating a new string with a new font comes from
samples/examples/example2.cc
. Many other examples of creating fonts and strings using them are found throughout the examples.Am_Font bold_font (Am_FONT_SERIF, true);
Am_Object oldness_label = Am_Text.Create("oldness_label")
.Set(Am_LEFT, 100)
.Set(Am_TOP, 65)
.Set(Am_TEXT, label_from_age_field)
.Set(Am_FONT, bold_font)
;
Amulet uses non-ASCII byte-codes for Japanese (and Chinese and Korean) input/output. (Both JIS (7 bit) and EUC are supported under X; SHIFT-JIS is supported under Windows-J.)
To output Japanese text, you must set a Japanese font into the Am_FONT
slot of an Am_Text
object, and set an appropriately encoded string into the Am_TEXT
slot. (Under X, Amulet uses the first byte of the string to distinguish between JIS and EUC encodings.) The following example is taken from amulet/src/opal/testJIO.cc
; the coding of the string title
, which is not shown, is JIS, EUC or S-JIS depending on platform.
Am_Object kanji = Am_Text.Create("kanjiE")
.Set (Am_TOP, 5)
.Set (Am_TEXT, title)
.Set (Am_FONT, Am_Japanese_Font);
Edit_Group.Add_Part(kanji);To input Japanese text under Windows-J, use an
Am_Text_Input_Widget
whose Am_FONT
slot has a Japanese font.
To construct a Japanese or other Asian font, you must specify one of the Asian font family parameters (e.g. Am_FONT_JFIXED
) and one of the Asian size parameters (e.g. Am_FONT_TMEDIUM
). Bold, italic and underline faces are available under Windows-J; italic is not available under X. The Am_FONT
, Am_Japanese_Font
, is exported from opal.h
; it is a fixed-width, medium-sized font.
For additional examples showing the use of Japanese fonts, see amulet/src/opal/testJIO.cc
.
Am_Text
objects, strings, and fonts declared in the header file text_fns.h
. These functions are included in the standard Amulet library, but are not automatically included by amulet.h
because of their infrequent use. The Am_Text_Interactor
uses these functions to edit strings. To access these functions directly, add the following lines to the top of your Amulet program:#include <am_inc.h> // defines TEXT_FNS__H for machine independence
#include TEXT_FNS__H
Am_Text_Interactor
. For details on this and other Interactors, see chapter 5.Am_Bitmap
object, you must first create an instance of Am_Image_Array
containing the image data, and then install the image in the Am_IMAGE
slot of your Am_Bitmap
object.
The formulas in the Am_WIDTH
and Am_HEIGHT
slots reevaluate whenever a new image is installed in the Am_IMAGE
slot. If a destructive modification is made to the image class currently installed in the slot, then Note_Changed()
will have to be called to cause the formulas to reevaluate and to cause the object to be redrawn (for details about destructive modification of slot values and formula evaluation, see the ORE chapter).
The Am_FILL_STYLE
is used to draw the background pixels of transparent images. If the Am_DRAW_MONOCHROME
slot is true
, the Am_FILL_STYLE
is used for the background pixels and the Am_LINE_STYLE
is used for the foreground pixels of any image. For transparent images the background pixels are the transparent pixels. For opaque images, the background pixels are usually the off pixels. The fill style and line style are always used for XBM images (SeeSection 4.6.8.1).
Am_Image_Array
is a regular C++ class, defined in gdefs.h
. Here is a list of the member functions that are available for the Am_Image_Array
class. The creator functions are used to make customized instances containing images described by data arrays or data stored in files. The other functions are for accessing, changing, and saving images to a file. Section 4.6.8.2 contains an example of how to use the Am_Image_Array
class with the Am_Bitmap
object. Section 4.7.3.7 discusses how to use the Am_Image_Array
class with Am_Style
.
Am_Image_Array (int percent);
// Halftone pattern (see Section 4.7.2.2)
Am_Image_Array (unsigned int width,
// Solid rectangular image
unsigned int height,
int depth,
Am_Style intial_color);
Am_Image_Array (const char* file_name);
// Image read from a file.
// The size of an image will be zero until drawn, & depends on the window in which the image is displayed.
void Get_Size (int& width, int& height);
Am_Image_Array
images can be loaded from several different image file formats depending on what platform you're using. Amulet decides which type of image to load based on the filename's suffix.Full color and transparent GIF images are available on all three platforms. Files with names ending in `gif' will be loaded as GIF images. Both 87a and 89a formats are supported, but only the first image in a file is loaded. Amulet reads the GIF colormap from the file, and provides the closest colors available on the display's current color map. Amulet does not install its own colormap, and Amulet does not support changing colormaps or image colors.
On Unix/ X machines, all GIF images are treated as multiple bit depth pixmaps (except on monochrome displays) and cannot be used as stipple fill styles.
PC BMP bitmap images are available only on Windows NT/ `95 machines. On the PC, files with names ending in `bmp' are loaded as Windows bitmap files.
XWindows XBM bitmap format is available only on Unix/ X machines. Under Unix, files with names that do not end in `gif' are assumed to be XBM format bitmaps. These bitmaps are loaded as single bit pixmaps under X, and can be used as stipple fill styles. Specifying foreground and background colors explicitly, and creating transparent bitmaps are possible with XBM format images.
To make your code as machine-independent as possible, you should use GIF image format whenever feasible to prevent conditional compilation of different image filenames. For code examples of using Am_Bitmap
, see the space
sample program in your Amulet directory.
Am_Image_Array
initialized with the name of the file, and then install that image in the Am_IMAGE
slot of your Am_Bitmap
object.Am_Style
is used to specify a set of graphical properties, such as color and line thickness. The Am_LINE_STYLE
and Am_FILL_STYLE
slots present in all graphical objects hold instances of Am_Style
. The definition of the Am_Style
class is in gdefs.h
, and the predefined styles are listed in opal.h
.
There are many predefined styles, like Am_Red
, that provide the most common colors and thicknesses. You can also create your own custom styles by calling the constructor functions for Am_Style
with your desired parameters. Whether you use a predefined style or create your own, you can set it directly into the appropriate slot of a graphical object. The style placed in the Am_LINE_STYLE
slot of a graphical object controls the drawing of lines and outlines, and the style in the Am_FILL_STYLE
slot controls the inside of the object.
Instances of Am_Style
are immutable, and cannot be changed after they are created.
Am_LINE_STYLE
or Am_FILL_STYLE
slot of an object.The following color styles have line thickness zero (which really means 1, as explained in Section 4.7.3.2)
The following styles are black.
The following styles are all black transparent or black and white opaque fills
Am_Style
creator function:Am_Style::Thick_Line (unsigned short thickness);
For example, if you wanted to create a black line style 5 pixels thick, you could say ``black5 = Am_Style::Thick_Line (5)
''. To specify the color or any other property simultaneously with the thickness, you have to use the full Am_Style
creator functions discussed in Section 4.7.3.Am_Style::Halftone_Stipple (int percent,
Am_Fill_Solid_Flag fill_flag = Am_FILL_STIPPLED);
The percent parameter determines the shade of the halftone (0 is white and 100 is black). The fill_flag determines whether the pattern is transparent or opaque (see Section 4.7.3.6). In order to create a halftone that is one-third black and two-thirds white, you could say ``gray33 = Am_Style::Halftone_Stipple (33)
''. There are only 17 different halftone shades available in Amulet, so several values of percent will map onto each built-in shade.Clone_With_New_Color
method to create a new style which takes all the properties from the style except the color, which it takes from the parameter. For example:Am_Style thick_red_line = Am_Line_8.Clone_With_New_Color(Am_Red);
Am_Style.
The properties are provided as parameters to the Am_Style
constructor functions. All the parameters have convenient defaults, so you only have to specify values for the parameters you are interested in. Since styles are used for both line styles and fill styles, some of the parameters only make sense for one kind of style or the other. The parameters that do not apply in a particular drawing situation are silently ignored.Am_Style (float red, float green, float blue, //color part
short thickness = 0,
Am_Line_Cap_Style_Flag cap_flag = Am_CAP_BUTT,
Am_Join_Style_Flag join_flag = Am_JOIN_MITER,
Am_Line_Solid_Flag line_flag = Am_LINE_SOLID,
const char* dash_list = Am_DEFAULT_DASH_LIST,
int dash_list_length = Am_DEFAULT_DASH_LIST_LENGTH,
Am_Fill_Solid_Flag fill_flag = Am_FILL_SOLID,
Am_Fill_Poly_Flag poly_flag = Am_FILL_POLY_EVEN_ODD,
Am_Image_Array stipple = Am_No_Image)
Am_Style (const char* color_name, //color part
short thickness = 0,
Am_Line_Cap_Style_Flag cap_flag = Am_CAP_BUTT,
Am_Join_Style_Flag join_flag = Am_JOIN_MITER,
Am_Line_Solid_Flag line_flag = Am_LINE_SOLID,
const char *dash_list = Am_DEFAULT_DASH_LIST,
int dash_list_length = Am_DEFAULT_DASH_LIST_LENGTH,
Am_Fill_Solid_Flag fill_flag = Am_FILL_SOLID,
Am_Fill_Poly_Flag poly_flag = Am_FILL_POLY_EVEN_ODD,
Am_Image_Array stipple = Am_No_Image)
static Am_Style Thick_Line (unsigned short thickness);
static Am_Style Halftone_Stipple (int percent,
Am_Fill_Solid_Flag fill_flag = Am_FILL_STIPPLED);
Am_Style Clone_With_New_Color (Am_Style& foreground);The only required parameters for these style constructors are for the colors. Before you read the details below about what all the other parameters mean, be aware that most applications will just use the default values.
Am_Style
constructor functions allow color to be specified in two ways: either with red, green, and blue values, or with a color_name such as ``pink''. The RGB values should be float
s between 0.0f
and 1.0f
, where 1.0f
is full on. Gem maintains a table of color names and their corresponding red, green, and blue values. In X, all standard X color names on your server are supported. A small common subset was chosen on the Mac and PC to get color indices.Amulet does not install its own colormap, to be friendlier to other applications on your system. Not all colors will be available on your platform. When a color is not available on your machine, Amulet finds the closest available color, and uses that instead. On Unix/ X, a warning is printed to your console saying that a color cell could not be allocated. Quitting applications that use many colors will help Amulet applications get the colors they need. On the PC, Windows automatically dithers solid colors together to get a closer match than possible with a single color. This sometimes produces undesirable results, but it is unavoidable.
For instances of Am_Rectangle
, Am_Roundtangle
, and Am_Arc
, increasing the thickness of the line style will not increase the width or height of the object. The object will stay the same size, but the colored boundary of the object will extend inwards to occupy more of the object. Increasing the thickness of the line style of an Am_Line
or Am_Polygon
(objects which calculate their bounding box, instead of having it provided by the user) will increase the object's width and height. For these objects the thickness will extend outward on both sides of the line or polyline.
Am_Line_Cap_Style_Flag
:Am_Line
) that are part of a group, even if they happen to have the same endpoints. Allowed values are elements of the enumerated type Am_Join_Style_Flag
:Am_Line_Solid_Flag
:
The dash_list and dash_list_length parameters describe the pattern for dashed lines. The dash_list should be a const char*
array that holds numbers corresponding to the pixel length of the ``on'' and ``off'' pixels. The default Am_DEFAULT_DASH_LIST value is {4 4}.
A dash_list of {1 1 1 1 3 1}
is a typical dot-dot-dash line. A list with an odd number of elements is equivalent to the list being appended to itself. Thus, the dash_list {3 2 1}
is equivalent to {3 2 1 3 2 1}
.
The following code defines a dash pattern with each ``on'' and ``off'' dash 15 pixels long. To see the result of this code, store the thick_dash
style in the Am_LINE_STYLE
slot of a graphical object.
static char thick_dash_list[2] = {15, 15};
Am_Style thick_dash ("black", 8, Am_CAP_BUTT, Am_JOIN_MITER,
Am_LINE_ON_OFF_DASH, thick_dash_list);Dashed and dotted lines are not supported on the Macintosh. In a future version of Amulet, we will either support the current implementation of dashed and dotted lines on all platforms, or more likely, we'll provide a few predefined dashed and dotted line styles. The dotted and dashed line styles are guaranteed to look different from each other and from normal lines, but we will not provide as much support for complex dashed lines. To maintain forward compatibility, you should only use the predefined dotted and dashed line styles
Am_Dashed_Line
and Am_Dotted_Line
.Am_Fill_Solid_Flag
:
The value of the poly_flag parameter should be an element of the enumerated type Am_Fill_Poly_Flag
, either Am_FILL_POLY_EVEN_ODD
or Am_FILL_POLY_WINDING
. This parameter controls the filling for self-intersecting polygons, like the five-pointed star example in Section 4.6.6.2.
Am_Style
creator functions when you are specifying some other property (like color) along with a non-solid stipple, or you are specifying an unconventional image for your stipple pattern.
The value of the stipple parameter should be an instance of Am_Image_Array
. An image array holds the pattern of bits, which can either be a standard halftone pattern or something more exotic. The creator functions and other member functions for Am_Image_Array
are discussed in Section 4.6.8.1.
Here is an example of a colored style with a 50% halftone stipple, created using the halftone initializer for Am_Image_Array
:
Am_Style red_stipple ("red", 8, Am_CAP_BUTT, Am_JOIN_MITER, Am_LINE_SOLID,
Am_DEFAULT_DASH_LIST, Am_DEFAULT_DASH_LIST_LENGTH,
Am_FILL_STIPPLED, Am_FILL_POLY_EVEN_ODD,
(Am_Image_Array (50)) );Here is an example of a stipple read from a file. The ``stripes'' file contains a description of a bitmap image. On Unix, the ``stripes'' file must be in X11 bitmap format (i.e., generated with the Unix
bitmap
utility). On the PC, the ``stripes'' file must be in either BMP
or GIF
format. This means that for portable code, you will need to use the #ifdef
macro to load different files depending on your platform.Am_Style striped_style ("black", 8, Am_CAP_BUTT, Am_JOIN_MITER,
Am_LINE_SOLID, Am_DEFAULT_DASH_LIST,
Am_DEFAULT_DASH_LIST_LENGTH, Am_FILL_STIPPLED,
Am_FILL_POLY_EVEN_ODD,
(Am_Image_Array ("stripes")) );
Here are the Am_Style
methods available for getting the values of a style's properties:
void Get_Values (float& red, float& green, float& blue) const;
void Get_Values (short& thickness,
Am_Line_Cap_Style_Flag& cap, Am_Join_Style_Flag& join,
Am_Line_Solid_Flag& line_flag,const char*& dash_l,
int& dash_l_length, Am_Fill_Solid_Flag& fill_flag,
Am_Fill_Poly_Flag& poly, Am_Image_Array& stipple) const;
void Get_Values (float& r, float& g, float& b, short& thickness,
Am_Line_Cap_Style_Flag& cap, Am_Join_Style_Flag& join,
Am_Line_Solid_Flag& line_flag, const char*& dash_l,
int& dash_l_length,Am_Fill_Solid_Flag& fill_flag,
Am_Fill_Poly_Flag& poly,Am_Image_Array& stipple) const;
Am_Fill_Solid_Flag Get_Fill_Flag() const;
Am_Image_Array Get_Stipple() const;
Am_Fill_Poly_Flag Get_Fill_Poly_Flag () const;
//Get the properties needed to calculate the line width
void Get_Line_Thickness_Values (short& thickness,
Am_Line_Cap_Style_Flag& cap) const;
const char* Get_Color_Name () const; //returns a pointer to the string, don't dealloc
Here are the slots of Am_Group:
Groups define their own coordinate system, meaning that the left and top of their parts is offset from the origin of the group. Changing the position of the group translates the position of all its parts. By using a special group called Am_Resize_Parts_Group
, you can also have the group scale the size of its parts when the group's size changes.
Groups also clip their parts to the bounding box of the group. Parts of objects that extend outside the left, top, width, or height of the group are not drawn. The default width and height of a group is 10x10, so you must be careful to set the Am_WIDTH
and Am_HEIGHT
slot of your instances of Am_Group
. The predefined constraints Am_Width_Of_Parts
and Am_Height_Of_Parts
may be used to compute the size of a group based on its parts
Am_GRAPHICAL_PARTS
slot of a group contains an Am_Value_List of objects. It can be used to iterate over the graphical parts of an object. Parts of a group should be manipulated with the following member functions defined on Am_Object
. (These are extensively discussed in the ORE Objects chapter).//
Adds a part to an object. The part either be named by providing a key or unnamed.
Am_Object& Add_Part (Am_Object new_part, bool inherit = true);
//
For the next method, slot must not already exist (unless Am_OK_IF_THERE flag).
Am_Object& Add_Part (Am_Slot_Key key, Am_Object new_part,
Am_Slot_Flags set_flags = 0);
//
Slot must exist (unless Am_OK_IF_THERE), and must already be a part slot
Am_Object& Set_Part (Am_Slot_Key key, Am_Object new_part,
Am_Slot_Flags set_flags = 0);
//
Removes a part from an object. The part can be named either by a key or by the actual part object.
//
Returns the original object so Remove_Part can be put into a chain of sets and add_parts, etc.
Am_Object& Remove_Part (Am_Slot_Key key);
Am_Object& Remove_Part (Am_Object part);
Graphical parts added to an object with Add_Part
or Set_Part
will automatically be added to the Am_GRAPHICAL_PARTS
list of the object.
Parts of an object may or may not be inherited (instantiated) in an instance of that object. All parts added with a slot key (using the second form of Add_Part
, above) to an owner are instantiated in instances of the owner. Parts added without a slot key (using the first form of Add_Part
, above) are only instantiated in instances of their owner if the second parameter, inherit, is true (the default is true).
Parts that are added with a slot key can be accessed with Get_Object()
or Get()
. It is often convenient to provide slot keys for parts so that functions and formulas can easily access these objects in their groups. All graphical parts can be accessed through the Am_GRAPHICAL_PARTS
list.
You can add any kind of object (graphical, non-graphical, windows, screens) as a part of any other object. However, things only ``work right'' if graphical objects are added to groups or windows, which are added to windows or the screen.
It is sometimes convenient for the group itself to decide where its graphical parts should be located. If the parts should all be in a row or column, it's often easier and more extensible to tell the group that all of its parts should be arranged in a specific way, than to try to keep track of the locations of all of the objects in the group individually. To support this, a formula in the Am_LAYOUT
slot of an Am_Group
object can lay out all of the parts of the group. This formula operates by directly setting the Am_LEFT
and Am_TOP
of the parts.
Am_Formula Am_Vertical_Layout
Am_Formula Am_Horizontal_LayoutThese layout procedures arrange the parts of a group according to the values in the slots listed below. To arrange the parts of a group in a vertical list (like a menu), set the
Am_LAYOUT
slot to Am_Vertical_Layout
. You may then want to set other slots of the group, like Am_V_SPACING,
to control things like the spacing between parts or the number of columns.
These procedures set values in the Am_LEFT
and Am_TOP
slots of the graphical parts of the group, overriding whatever values (or formulas) were there before.
The slots that control layout when using the standard vertical or horizontal layout procedures are:
Am_LEFT_OFFSET
The number of pixels for a border to leave on the left (default is 0
).
Am_TOP_OFFSET
The number of pixels for a border to leave on the top (default is 0
).
Am_RIGHT_OFFSET
The number of pixels for a border to leave on the right (default is 0
).
Am_BOTTOM_OFFSET
The number of pixels for a border to leave on the bottom (default is 0
).
Am_H_SPACING
The horizontal space to leave between parts, measured in pixels (default is 0
)
Am_V_SPACING
Same as Am_H_SPACING
, only vertical (default is 0
)
Am_H_ALIGN
Justification for parts within a column: when a narrow part appears in a column with other wider parts, this parameter determines whether the narrow part is positioned at the left, center, or right of the column (default is Am_CENTER_ALIGN
)
Am_V_ALIGN
Same as Am_H_ALIGN
, only vertical (default is Am_CENTER_ALIGN
)
Am_FIXED_WIDTH
The width of each column, probably based on the width of the widest part. When Am_NOT_FIXED_SIZE
is used, the columns are not necessarily all the same width; instead, the width of each column is determined by the widest part in that column. (default is Am_NOT_FIXED_SIZE
)
Am_FIXED_HEIGHT
Same as Am_FIXED_WIDTH
, only vertical (default is Am_NOT_FIXED_SIZE
)
Am_INDENT
How much to indent the second row or column (depending on horizontal or vertical orientation), measured in number of pixels (default is 0)
Am_MAX_RANK
The maximum number of parts allowed in a row or column, depending on horizontal or vertical orientation (default is false
)
Am_MAX_SIZE
The maximum number of pixels allowed for a row or column, depending on horizontal or vertical orientation (default is false
)
Am_Object my_group = Am_Group.Create ("my_group")
.Set (Am_LEFT, 10)
.Set (Am_TOP, 10)
.Set (Am_LAYOUT, Am_Vertical_Layout)
.Set (Am_V_SPACING, 5)
.Add_Part(Am_Rectangle.Create())
.Add_Part(Am_Circle.Create());
Am_LAYOUT
slot of the group. The parts of the group should be arranged as a side effect of evaluating the formula (the return value is ignored). To do this, Get
the list in the Am_GRAPHICAL_PARTS
slot (which is in Z-order) and iterate through it, setting each part's Am_LEFT
and Am_TOP
slots appropriately.Am_Group
except that if the width or height of the Am_Resize_Parts_Group
is changed, then the width and height of all the components is scaled proportionately. The width and height of the Am_Resize_Parts_Group
should not be a formula depending on the parts, and the parts should not have formulas in their width and height slots. Instead, the width and heights will usually be integers, with the original size of the group set to be the correct size based on the parts. Be sure to adjust the width and height of a Am_Resize_Parts_Group
when new parts are added or removed. Since you cannot use formulas that depend on the parts' sizes, you must explicitly make sure the group is always big enough to hold its parts.
All of the parts of a Am_Resize_Parts_Group
are expected to be able to resize themselves when their width and heights are set. It is fine to use a Am_Resize_Parts_Group
in another one, but it would usually be an error to include a regular Am_Group
inside a Am_Resize_Parts_Group
.
The Am_Resize_Parts_Group
is created automatically by the Am_Graphics_Group_Command
when the user selects a number of objects and executes a ``Group'' command (see Section 7.4 in the Widgets chapter).
The Am_Fade_Group
works just like the regular Am_Group
in the way that parts are added and displayed. The difference is that the Am_VALUE
slot of the Am_Fade_Group
can be set to any value from 0 to 100 to cause all the objects to ``fade out'' using a halftoning trick. 100 (the default) means fully visible, and 0 means fully invisible. This can used with the Am_Visible_Animator
(see the Animation chapter) to fade out objects when the become invisible, and it has also been used to create ``shadows'' by copying and fading out objects at 50%.
The Am_Flip_Book_Group
chooses one of the parts to show. Use the regular Add_Part
method to add parts to a flip book. The Am_VALUE
slot of the Am_Flip_Book_Group
itself determines which one part will be displayed. To display different parts, set the Am_Flip_Book_Group
's Am_VALUE
to the desired part's index (which starts from zero which is the first part added). Do not use an Am_LAYOUT
constraint with a Am_Flip_Book_Group
Am_Map
object is a special kind of group that generates multiple graphical parts from a single prototype object. Maps should be used when all the parts of a group are similar enough that they can be generated from one prototype object (for example, they are all rectangles, or all the same kind of group.). This part-generating feature of maps is often used in conjunction with the layout feature of groups, in a situation such as arranging the selectable text items in a menu. For details on laying out the components of groups and maps, see Section 4.8.2.You must set two slots in the map to control the parts that are generated:
Am_ITEMS
The value should be either a number, specifying how many parts should be generated, or an instance of Am_Value_List
, containing elements corresponding to each part to be generated.
Am_ITEM_PROTOTYPE
A graphical group, to serve as the prototype for each part.
Am_RANK
The position of this part in the list, from 0
Am_ITEM
The element of the map's Am_ITEMS
list that corresponds to this part
Am_RANK
of each created part is set with the count of this part. The Am_RANK
of the first part created is set to 0, the second part's Am_RANK
is set to 1, and so on. If the Am_ITEMS
slot of the map contains an Am_Value_List
, then the Am_ITEM
(note: singular) slot of each created part is set with the corresponding element of the list.
The following code defines a map whose Am_ITEMS
slot is a number. The map generates 4 rectangles, whose fill styles are determined by the formula map_fill_from_rank
. The formula computes a halftone fill from the value stored in the Am_RANK
slot of the part, which was installed by the map as the part was created. This uses a horizontal layout formula so the rectangles will be in a row.
// Formulas are defined in the global scope, outside of main()The next example defines a map whose
Am_Define_Style_Formula (map_fill_from_rank) {
int rank = self.Get (Am_RANK);
return Am_Style::Halftone_Stipple (20 * rank);
}
...
// This code is inside main()
Am_Object my_map = Am_Map.Create ("my_map")
.Set (Am_LEFT, 10)
.Set (Am_TOP, 10)
.Set (Am_LAYOUT, Am_Horizontal_Layout)
.Set (Am_H_SPACING, 5)
.Set (Am_ITEMS, 4)
.Set (Am_ITEM_PROTOTYPE, Am_Rectangle.Create ("map item")
.Set (Am_FILL_STYLE, map_fill_from_rank)
.Set (Am_WIDTH, 20)
.Set (Am_HEIGHT, 20));
Am_ITEMS
slot contains a list of strings. The map generates 4 text objects, whose text strings are determined by the object's Am_ITEM
slot.// This code is inside main()
Am_Object my_map = Am_Map.Create ("my_map")
.Set (Am_LEFT, 10)
.Set (Am_TOP, 10)
.Set (Am_LAYOUT, Am_Vertical_Layout)
.Set (Am_V_SPACING, 5)
.Set (Am_ITEMS, Am_Value_List ()
.Add (``This is the first item in the map.'')
.Add (``I'm number two'')
.Add (``Three'')
.Add (``The last item in the list.''))
.Set (Am_ITEM_PROTOTYPE, Am_Text.Create ("map text item")
.Set (Am_ITEM, ````) // initialize the slot so the formula won't crash
.Set (Am_TEXT, Am_Same_As(Am_ITEM))
);To add another item to the map in the second example, you could install a new list in the
Am_ITEMS
slot containing all the old items plus the new one:my_map.Make_Unique (Am_ITEMS); //in case slot shared with another object
Am_Value_List map_items = (Am_Value_List) my_map.Get (Am_ITEMS);
map_items.Add (``A new item!'');
my_map.Set (Am_ITEMS, map_items);A more efficient way to add an item to the list is to destructively modify the list that is already installed (note the use of the false parameter in the
Add
method for Am_Value_List
):Am_Value_List map_items = (Am_Value_List) my_map.Get (Am_ITEMS);
map_items.Add (``A new item!'', false); // false means destructively modify, don't copy.
my_map.Note_Changed (Am_ITEMS);The list in the
Am_ITEMS
slot can also be calculated with a formula, and the items in the map will change whenever the formula is reevaluated.
The following functions are useful for changing the Z order of an object among its siblings. For example, Am_To_Top(obj)
will bring an object to the front of all of the other objects in the same group or window. To promote an object just above a certain target object, use Am_Move_Object (obj, target_obj, true)
. These functions work for windows as well as for regular graphical objects.
void Am_To_Top (Am_Object object);
void Am_To_Bottom (Am_Object object);
void Am_Move_Object (Am_Object object, Am_Object ref_object,
bool above = true); // false means below ref_object
bool Am_Point_In_All_Owners(Am_Object in_obj, int x, int y,
Am_Object ref_obj);
Am_Object Am_Point_In_Obj (Am_Object in_obj, int x, int y,
Am_Object ref_obj);
Am_Object Am_Point_In_Part (Am_Object in_obj, int x, int y,
Am_Object ref_obj,
bool want_self = false,
bool want_groups = true);
Am_Object Am_Point_In_Leaf (Am_Object in_obj, int x, int y,
Am_Object ref_obj,
bool want_self = true,
bool want_groups = true);These functions all use the
Am_HIT_THRESHOLD
and Am_PRETEND_TO_BE_LEAF
slots. See Section 4.4.4.
Am_Point_In_Obj
checks whether the point is inside the object. It ignores covering (i.e., it just checks whether point is inside the object, even if the object is behind another object). If the point is inside, the object is returned; otherwise the function returns Am_No_Object
. The coordinate system of x and y is defined with respect to ref_obj
, that is, the origin of x and y is the left and top of ref_obj
.
Am_Point_In_Part()
finds the front-most (least covered) immediate part of in_obj
at the specified location. If there is no part at that point, it returns Am_No_Object
. The coordinate system of x and y is defined with respect to ref_obj. If there is no part at that point, then if want_self
then if inside in_obj
, returns in_obj
. If not want_self
or not inside in_obj
, returns Am_No_Object
. The coordinate system of x and y is defined with respect to ref_obj. If want_groups
is true, then returns the part even if it is a group. If want_groups
is false, then will not return a group (so if x, y is not over a ``primitive'' object, returns Am_No_Object
).
Am_Point_In_Leaf()
is similar to Am_Point_In_Part()
, except that the search continues to the deepest part in the group hierarchy (i.e., it finds the leaf-most object at the specified location). If (x, y) is inside the bounding box of in_obj
but not over a leaf, it returns in_obj. The coordinate system of x and y is defined with respect to ref_obj. Sometimes you will want a group to be treated as a leaf in this search even though it isn't really a leaf. In this case, you should set the Am_PRETEND_TO_BE_LEAF
slot to true for each group that should be treated like a leaf. The search will not look through the parts of such a group, but will return the group itself. The slots want_self
and want_groups
work the same as for Am_Point_In_Part
.
Am_Point_In_Part()
and Am_Point_In_Leaf()
use the function Am_Point_In_Obj()
on the parts.
void Am_Beep (Am_Object window = Am_No_Object);This function causes the computer to emit a ``beep'' sound. Passing a specific window is useful in Unix, when several different screens might be displaying windows, and you only want a particular screen displaying a particular window to beep.
char *Am_Merge_Pathname (char *name);
Am_Merge_Pathname()
takes a filename as a parameter, and returns the full Amulet directory pathname prepended to that argument. For example, ``Am_Merge_Pathname (``lib/images/ent.bmp'')
''
will return the full pathname to the PC compatible Enterprise bitmap included with the Amulet source files.On the Macintosh, Am_Merge_Pathname automatically converts the Unix-standard path separation character ``/'' into the Mac-specific path separator ``:''. In Windows NT/'95, this conversion is performed automatically by the OS. On all systems, you should specify pathnames with slashes (``/'') as the path separator to avoid machine dependency.
bool Am_Translate_Coordinates (Am_Object src_obj, int src_x, int src_y,
Am_Object dest_obj, int& dest_x, int& dest_y);
Am_Translate_Coordinates()
converts a point in one object's coordinate system to that of another object. It works for windows and all graphical objects. If the objects are not comparable (windows on separate screens, or windows not attached to any screen) then the function will return false
. Otherwise, it will return true
and dest_x and dest_y will contain the converted coordinates. The destination coordinates are for the inside of dest_obj. This means that if obj was at src_x, src_y with respect to the left and top of src_obj, and you remove it from src_obj and add it to dest_obj at dest_x, dest_y then it will be at the same physical screen position.
Since each group and window defines its own coordinate system, you must use Am_Translate_Coordinates
whenever you define a formula that depends on the left or top of an object that might be in a different group or window. If this function is called from inside a constraint, then it will set up constraints so that if any of the objects or windows move, then the constraint will be re-evaluated.
Add_Part()
. All graphical objects added to a window will be displayed in that window. When a window is added as a part to another window, it becomes a subwindow. Subwindows do not have any window manager decoration (title bars).Am_LEFT
, Am_TOP
, Am_WIDTH
, and Am_HEIGHT
determine the size and position of the window when it appears on the screen. These slots can be set later to change the window's size and position. If the user changes the size or position of a window using the window manager (e.g., using the mouse), this will be reflected in the values for these slots.Note that under Unix/X, it's not always possible to know exactly where a window is on the screen. Some window managers specify screen position as the location of the titlebar, some specify it as the location of the client region, and some allow the user to choose the coordinate reference system. It's impossible for Amulet to enumerate all the possible things that a window manager might do, and take them into account. In this case, our goal is to have code that never breaks and that maintains internal consistency.
The Am_FILL_STYLE
determines the background color of the window. All parameters of Am_Style
that affect fillings, including stipples, affect the fill style of windows. Using the fill style of a window is more efficient than putting a window-sized rectangle behind all the other objects in the window.
When values are installed in the Am_MAX_WIDTH
, Am_MAX_HEIGHT
, Am_MIN_WIDTH
, or Am_MIN_HEIGHT
slots, and the corresponding Am_USE_MAX_WIDTH
, Am_USE_MAX_HEIGHT
, Am_USE_MIN_WIDTH
, or Am_USE_MIN_HEIGHT
slot is set to true
, then the window manager will make sure the user is not allowed to change the window's size to be outside of those ranges. You can still set the Am_WIDTH
and Am_HEIGHT
to be any value, but the window manager will eventually clip them back into the allowed range. Windows that use the max and min width, and where the max == min do not display grow handles (at least on the Macintosh).
When Am_QUERY_POSITION
or Am_QUERY_SIZE
are set to true
, then the user will have the opportunity to place the window on the screen when the window is first added to the screen, clicking the left mouse button to position the left and top of the window, and dragging the mouse to the desired width and height.
The border widths applied to the window by the window manager are stored in the Am_LEFT_BORDER_WIDTH
, Am_TOP_BORDER_WIDTH
, Am_RIGHT_BORDER_WIDTH
, and Am_BOTTOM_BORDER_WIDTH
. These slots are read only, set by Amulet when the window becomes visible on the screen.
The Am_OMIT_TITLE_BAR
slot tells whether the Amulet window should have a title bar. If the slot has value false
(the default), and the window manager permits it, then the window will have a title bar; otherwise the window will not have a title bar.
The Am_ICONIFIED
slot tells whether the window is reduced to an icon (mainly useful under Unix), and can be set to explicitly turn the window into just an icon.
Under Unix/X and PC/Windows, if the value of the Am_ICON_TITLE
slot is Am_No_Value
, then the value of the Am_TITLE
slot is used as the title of the iconified window.
Use the Am_CURSOR
slot to change the appearance of the cursor. If the slot has the value Am_Default_Cursor
then the default cursor for the window (usually an arrow) is used.
In the rare case when you want to have graphics drawn on a parent window appear over the enclosed (child) windows, you can set the Am_CLIP_CHILDREN
slot of the parent to be true
. Then any objects that belong to that window will appear on top of the window's subwindows (rather than being hidden by the subwindows).
When the Am_DOUBLE_BUFFER
slot is set to true (the default), Opal updates the window by first drawing everything offscreen, and then copying the region over the old contents of the window. This is slightly slower than updating without double buffering and it uses a lot of memory, because you need to copy the contents of the offscreen buffer back to the screen. However, using double buffering is much more visually pleasing, and flicker is reduced tremendously, because you don't see each object being drawn to the screen in succession; it all appears at once.
Am_Cursor
is a C++ class defined in gdefs.h
. It is used to make customized cursors. Its constructor takes four arguments:Am_Cursor(Am_Image_Array image, Am_Image_Array mask,
Am_Style fg_color, Am_Style bg_color);Under X-Windows the
image
and mask
must be X bitmaps, and the fg_color
and bg_color
are used for the on and off pixels, respectively. Under Windows the foreground and background colors are ignored.Am_DESTROY_WINDOW_METHOD
slot of Am_WINDOW
holds an Am_Object_Method
that is called when the window is destroyed. The default method is:
Other predefined methods may be useful for various applications (defined in opal.h
):Am_Window_Hide_Method
makes the window be invisible and does not destroy it, which is useful for dialog boxes.
Am_Window_Destroy_And_Exit_Method
always destroys the window and quits the application. This might be useful for an application's main window.
Am_Default_Pop_Up_Window_Destroy_Method
(this one is defined in widgets.h): makes the window invisible, and then calls Am_Finish_Pop_Up_Waiting
() returning Am_No_Value
. Use this for dialog boxes and other windows which are popped up using Am_Pop_Up_Window_And_Wait
. This is the default destroy method for windows created with Gilt.
win.Destroy()
. This does not cause the Am_DESTROY_WINDOW_METHOD
to be called. That is only called when the user destroys the window using the window manager's commands.Am_Screen
object can be thought of as a root window to which all top-level windows are added. In the ``hello world'' example of Section 4.3.2, the top-level window is added to Am_Screen
with a call to Add_Part()
.On X/11, you can create screen for multiple displays at the same time, so one application can be displaying on two different computers. This is done by creating a new screen using the function:
Am_Object Am_Create_Screen (const char* display_name);This function takes the display name of the new screen to open and returns a screen object to which windows can be added. Be sure that you have run xhost in the shell to give permission to open the display. The default Am_Screen is always based on the shell's DISPLAY environment variable set with setenv. The following is a simple example. The samples/checkers.cc has a more extensive example.
Am_Screen.Add_Part(Am_Window.Create()); //This window is on the ``main'' screen
Am_Object other_screen = Am_Create_Screen(``gem.amulet.cs.cmu.edu:0.0'');
other_screen.Add_Part(Am_Window.Create()); //This window is on gem
Am_Fill_To_Bottom
- Put in an object's Am_HEIGHT
slot, causes the object to size itself so it's tall enough to fill to the bottom of its owner. Am_Fill_To_Bottom
leaves a border below the object, with a size equal to the object's Am_BOTTOM_OFFSET
slot.
Am_Fill_To_Right
- Analogous to Am_Fill_To_Bottom
, used in the Am_WIDTH
slot of an object. The Am_RIGHT_OFFSET
slot of the object is used to measure the border to the right of the object.
Am_Fill_To_Rest_Of_Width
- sums up the width of all the other parts of owner, and makes this object's width be whatever is left over. This only works when the owner's width does not depend on the width of the parts, and all the parts of the owner must be non-overlapping. For example, this formula is useful for when the owner uses Am_LAYOUT
of Am_Horizontal_Layout
. Takes into account Am_LEFT_OFFSET
(on the left) and Am_RIGHT_OFFSET
(on the right).
Am_Fill_To_Rest_Of_Height
- same as Am_Fill_To_Rest_Of_Width
except for height. Uses the Am_TOP_OFFSET
and Am_BOTTOM_OFFSET
slots.
Am_Width_Of_Parts
- Useful for computing the width of a group: returns the distance between the group's left and the right of its rightmost part. Adds in the value of the object's Am_RIGHT_OFFSET
slot if it exists. You might put this into a group's Am_WIDTH
slot.
Am_Height_Of_Parts
- Analogous to Am_Width_Of_Parts
, but for the Am_HEIGHT
of a group. Uses Am_BOTTOM_OFFSET
as the border if it exists.
Am_Right_Is_Right_Of_Owner
- Useful for keeping a part at the right of its owner. Put this formula in the Am_LEFT
slot of the part. Leaves a border of Am_RIGHT_OFFSET.
Am_Bottom_Is_Bottom_Of_Owner
- Useful for keeping a part at the bottom of its owner. Put this formula in the Am_TOP
slot of the part. Leaves a border of Am_BOTTOM_OFFSET
.
Am_Center_X_Is_Center_Of_Owner
- Useful for centering a part horizontally within its owner. Put this formula in the Am_LEFT
slot of the part.
Am_Center_Y_Is_Center_Of_Owner
- Useful for centering a part vertically within its owner. Put this formula in the Am_TOP
slot of the part.
Am_Center_X_Is_Center_Of
- Useful for horizontally centering obj1
inside obj2
. Put this formula in the Am_LEFT
slot of obj1
, and put obj2
in the Am_CENTER_X_OBJ
slot of obj1
.
Am_Center_Y_Is_Center_Of
- Useful for vertically centering obj1
inside obj2
. Put this formula in the Am_TOP
slot of obj1
, and put obj2
in the Am_CENTER_Y_OBJ
slot of obj1
.
Am_Horizontal_Layout
- Constraint which lays out the parts of a group horizontally in one or more rows. Put this into the Am_LAYOUT
slot of a group.
Am_Vertical_Layout
- Constraint which lays out the parts of a group vertically in one or more columns. Put this into the Am_LAYOUT
slot of a group.
Am_Same_As (Am_Slot_Key key, int offset = 0, float multiplier = 1.0)
- The slot gets its value from the specified slot (key
) in the same object. If the offset or multiplier is provided, then the value must be numeric and the formula is equivalent to {return (self.Get(key)*multiplier) + offset;}
. If there is no offset or multiplier, then Am_Same_As
can be used for any type of slot.
Am_From_Owner (Am_Slot_Key key, int offset = 0, float multiplier = 1.0)
- The slot gets its value from the specified slot (key
) in the object's owner. If there is no offset or multiplier, then equivalent to {return self.Get_Owner().Get(key);}
. If the offset or multiplier is provided, then the value must be numeric.
Am_From_Part (Am_Slot_Key part, Am_Slot_Key key, int offset = 0, float multiplier = 1.0)
- This slot gets its value from the specified slot (key
) in the specified part (part
) of this object. If there is no offset or multiplier, then equivalent to {return self.Get_Object(part).Get(key);}
. If the offset or multiplier is provided, then the value must be numeric.
Am_From_Sibling (Am_Slot_Key sibling, Am_Slot_Key key, int offset = 0, float multiplier = 1.0)
- This slot gets its value from the specified slot (key
) in the specified sibling (sibling
) of this object. If there is no offset or multiplier, then this constraint is equivalent to the expression {return self.Get_Sibling(sibling).Get(key);
If the offset or multiplier is provided, then the value must be numeric.
Am_From_Object (Am_Object object, Am_Slot_Key key, int offset = 0, float multiplier = 1.0)
- This slot gets its value from the specified slot (key
) of the specified (constant) object
. Offset and multiplier can be supplied as in the other formulas.