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_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
4.2 The Opal Layer of Amulet
Opal, which stands for the Object Programming Aggregate Layer, provides simple graphical objects for use in the Amulet environment. The goal of Opal is to make it easy to create and edit graphical objects. To this end, Opal provides default values for all of the properties of objects, so simple objects can be drawn by setting only a few parameters. If an object is changed, Opal automatically handles refreshing the screen and redrawing that object and any other objects that may overlap it. Objects in Opal can be connected together using constraints, which are relations among objects that are declared once and automatically maintained by the system. An example of a constraint is that a line must stay attached to a rectangle. Constraints are discussed in the Tutorial and the ORE chapter.
To use Opal, the programmer should be familiar with Amulet objects and constraints as presented in the Tutorial. Opal is part of the Amulet library, so all objects discussed in this chapter are accessible by linking the Amulet library to your program (see the Overview for instructions). Opal will work with any window manager on top of X/11, such as mwm, uwm, twm, etc. Opal provides support for color and gray-scale displays.
4.3 Basic Concepts
4.3.1 Windows, Objects, and Groups
X/11, Windows NT, and Mac Quickdraw all allow you to create windows on the screen. In X they are called ``drawables'', and in Windows NT and on the Mac they are just called ``windows''. An Opal window is an ORE data structure that contains pointers to these machine-specific structures. Opal windows can be nested inside other windows to form subwindows. Windows clip all graphics so they do not extend outside the window's borders.
The basics of Object Oriented Programming are beyond the scope of this chapter. The objects in Opal use the ORE object system, and therefore operate as a prototype-instance model. This means that each object can serve as a prototype (like a class) for any further instances; there is no distinction between classes and instances. Each graphic primitive in Opal is implemented as an object. When the programmer wants to cause something to be displayed in Opal, it is necessary to create instances of these graphical objects. Each instance remembers its properties so it can be redrawn automatically if the window needs to be refreshed or if objects change.
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.Am_Window
or a Am_Group
(or an instance of thse objects).4.3.2 The ``Hello World'' Example
An important goal of Opal is to make it simple to create pictures, hiding most of the complexity of the machine dependant graphics toolkits. We provide default values for all of the properties of graphical objects in Amulet, which makes it easy to use these objects in the most common way, while still providing the option of complete customization for more specialized applications.#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 directory samples/hello/
. The same source code is used on all three platforms: Unix/X, Windows NT/'95, and Mac.
4.3.3 Initialization and Cleanup
Amulet requires a call to 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.4.3.4 The Main Event Loop
In order for interactors to perceive input from the mouse and keyboard, the main-event-loop must be running. This loop constantly checks to see if there is an event, and processes it if there is one. The automatic redrawing of graphics also relies on the main-event-loop. Exposure events, which occur when one window is uncovered or exposed, cause Amulet to refresh the window by redrawing the objects in the exposed area.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.4.3.5 Am_Do_Events
Amulet does not yet have support for animation, or other mechanisms for connecting to outside applications and databases. To implement these using Amulet, you have to write your own event loop (instead of using Am_Main_Event_Loop
) and perform the external or time-based actions yourself. This section explains how to write your own loop.Am_Do_Events()
to cause Amulet to update the screen based on all the changes that have happened so far. Eventually, Amulet will contain an Animation Interactor, but it is not implemented yet.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 loo. 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 ();
}
4.4 Slots Common to All Graphical Objects
4.4.1 Left, Top, Width, and Height
All graphical objects have 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.Am_LEFT, Am_TOP, Am_WIDTH,
and Am_HEIGHT
of the object.4.4.2 Am_VISIBLE
The 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
.4.4.3 Line Style and Filling Style
The 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.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.6.
Using special value
Am_No_Style
or NULL
in the Am_LINE_STYLE
or Am_FILL_STYLE
slot will cause the object to have no border or no fill.4.4.4 Am_HIT_THRESHOLD and Am_PRETEND_TO_BE_LEAF
The 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.Am_VISIBLE
slot contains false
, it will not respond to events such as mouse clicks with conventional Interactors programming techniques.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.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.9.2 regarding the function Am_Point_In_Leaf
. Also, consult the Interactors chapter regarding the function Am_Inter_In_Leaf
.
4.5 Specific Graphical Objects
The descriptions in this section highlight aspects of each object that differentiate it from other objects. Some properties of Opal objects are similar for all objects, and are documented in Section 4.4. All of the exported objects in Amulet are summarized in chapter 10.
4.5.1 Am_Rectangle
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.4.5.2 Am_Line
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.4.5.3 Am_Arc
Am_Arc is used to draw circles, ellipses, and arc and pie shaped segments of circles and ellipses. Am_ANGLE1 determines the origin of the segment, measured in degrees counterclockwise from 3 o'clock. Am_ANGLE2 specifies the end of the arc segment, measured in degrees counterclockwise from Am_ANGLE1. Some examples: To draw a circle, Am_ANGLE1 can have any value, but Am_ANGLE2 must be 360 degrees or greater. To draw a semicircle from 12 o'clock counterclockwise to 6 o'clock, Am_ANGLE1 would be 90, and Am_ANGLE2 would be 180.4.5.4 Am_Roundtangle
Instances of the Am_Roundtangle
prototype are rectangles with rounded corners.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.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.
4.5.5 Am_Polygon
The interface to the 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.Am_Point_List
class, including how to create point lists and add points to the list. Section 4.5.5.2 provides an example of how to create a polygon using the Am_Polygon
object and the Am_Point_List
class.
A polygon's point list (
Am_POINT_LIST
) and bounding box (Am_LEFT
, Am_TOP
, Am_WIDTH
, and Am_HEIGHT
) are related by a web constraint that reevaluates whenever one of these slots changes. Whenever one of the bounding box slots is changed, all the points in the point list are translated or scaled so that the resulting polygon has that bounding box. The Am_LINE_STYLE
slot is also involved in this constraint, since the polygon's bounding box may change when the thickness or cap style of its border changes.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.11.1.)
Because of the web in the polygons' bounding box and point list slots, the effects of certain
Set
operations will change depending on the order of the Sets
. If you set the Am_POINT_LIST
slot of a polygon, and then set its Am_WIDTH
and Am_HEIGHT
, the polygon will first get the correct point list, and then be scaled to the correct size. If you set the size first, however, that change will be masked by the calculate size of the point list. You should always make changes to the size and location of a polygon after you set its point list, to make sure those changes are actually seen.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.Am_Fill_Poly_Flag
part of the Am_FILL_STYLE
used for the polygon. See Section 4.6.3.6.
4.5.5.1 The Am_Point_List Class
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.8). 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.
In addition to the standard list-management functions,
Am_Point_List
also provides methods for finding the points' bounding box, translating all the points by an x-y offset, and scaling all the points relative to an origin.Am_Point_List
is designed to be used to specify the vertices of a polygon object. Section 4.5.5.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
:
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_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:
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_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_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 independance
#include TEXT_FNS__H
Am_Text_Interactor
. For details on this and other Interactors, see chapter 5.
4.5.7 Am_Bitmap
To specify the image that should be drawn by the 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.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).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.5.7.2 contains an example of how to use the Am_Image_Array
class with the Am_Bitmap
object. Section 4.6.3.7 discusses how to use the Am_Image_Array
class with Am_Style
.
Am_Image_Array (int percent);
// Halftone pattern (see Section 4.6.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);
4.5.7.1
Loading Am_Image_Arrays From a File
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.Am_Bitmap
with these file formats, see the space
sample program in your Amulet directory.4.5.7.2 Using Images with Am_Bitmap
To display an image whose description is stored in a file, you must first create an instance of 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.4.6 Styles
The C++ class 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
.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.Am_Style
are immutable, and cannot be changed after they are created.4.6.1 Predefined Styles
The most frequently used styles are predefined by Amulet. You can use any of the styles listed in this section directly in the Am_LINE_STYLE
or Am_FILL_STYLE
slot of an object.
The following styles are black.
4.6.2 Creating Simple Line and Fill Styles
4.6.2.1 Thick Lines
To quickly create black line styles of a particular thickness, you can use the following special Am_Style
creator function:
For example, if you wanted to create a black line style 5 pixels thick, you could say ``Am_Style::Thick_Line (unsigned short thickness);
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.6.3.
4.6.2.2 Halftone Stipples
Stippled styles repeat a pattern of ``on'' and ``off'' pixels throughout a line style or fill style. A halftone is the most common type of stipple pattern, where the ``on'' and ``off'' bits are regularly spaced to create darker or lighter shades of a color. When mixing black and white pixels, for example, a 50% stipple of black and white bits will look gray. A 75% stipple will look darker, and a 25% stipple will look lighter. Some gray stipples are predefined in Amulet, and listed in Section 4.6.1. More complicated stipples, such as diamond patterns, are discussed in Section 4.6.3.7.
To create a simple halftone style with a regular stipple pattern, use this special
Am_Style
creator function:
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.6.3.6). In order to create a halftone that is one-third black and two-thirds white, you could say ``Am_Style::Halftone_Stipple (int percent,
Am_Fill_Solid_Flag fill_flag = Am_FILL_STIPPLED);
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.
4.6.3 Customizing Line and Fill Style Properties
Any property of a style can be specified by creating an instance of 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);
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.4.6.3.1 Color Parameter
The 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.4.6.3.2 Thickness Parameter
The thickness parameter holds the integer line thickness in pixels. Zero thickness lines are drawn with line thickness one. On some platforms, there may be a subtle difference between lines with thickness zero and lines with thickness one. Zero thickness lines might be drawn on some platforms with a more efficient device-dependent line drawing algorithm than is used for lines with thickness of one or greater. For this reason, a thickness zero line parallel to a thick line may not be as aesthetically pleasing as a line with thickness one.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.4.6.3.3 Cap_Flag Style Parameter
The cap_flag parameter determines how the end caps of line segments are drawn in X11. This parameter is ignored on the PC and Mac. Allowed values are elements of the enumerated type Am_Line_Cap_Style_Flag
:4.6.3.4 Join_Flag Style Parameter
The join_flag parameter determines how corners (where multiple lines come together) are drawn for thick lines as part of rectangle and polygon objects in X11. This parameter is ignored on the PC. This does not affect individual lines (instances of 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
:4.6.3.5 Dash Style Parameters
The line_flag parameter determines whether the line is solid or dashed, and how the spaces between the dashes should be drawn. Valid values are elements of the enumerated type Am_Line_Solid_Flag
: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}
.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 compatability, you should only use the predefined dotted and dashed line styles Am_Dashed_Line
and Am_Dotted_Line
.4.6.3.6 Fill Style Parameters
The fill_flag determines the way ``off'' pixels in the stippled pattern (see Section 4.6.3.7) will be drawn. The ``on'' pixels are always drawn with the color of the style. Allowed values are elements of the enumerated type 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.5.5.2.
4.6.3.7 Stipple Parameters
A stippled style consists of a small pattern of ``on'' and ``off'' pixels that is repeated throughout the border or filling of an object. The simplest stipple pattern is the halftone, discussed in Section 4.6.2.2. You should only need to specify the stipple parameter in the full 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.5.7.1.
On Unix/X, only XBM images can be used as stipples. GIF stipples and transparent GIFs are not currently supported on Unix.
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")) );
4.6.4 Getting Style Properties
You can query certain properties of an already-created style. This is useful if you want to create a style with the same color as another style, but with a different line thickness, for example.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;
void Get_Line_Thickness_Values (short& thickness,
//Get the properties needed to calculate the line width
Am_Line_Cap_Style_Flag& cap) const;
const char* Get_Color_Name () const; //returns a pointer to the string, don't dealloc
4.7 Groups
Groups hold a collection of graphical objects (possibly including other groups). The objects in a group are called its parts, and the group is the owner of each of its parts. The concept of part/owner relationships was introduced in the ORE chapter, but groups treat their parts specially, by drawing them in windows. In Opal, the part-owner hierarchy corresponds to a graphical hierarchy, when graphical objects are involved.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 parts4.7.1 Adding and Removing Graphical Objects
The read only 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
:Am_Object Add_Part (Am_Object new_part, bool inherit = true);
Am_Object Add_Part (Am_Slot_Key key, Am_Object new_part);
void Remove_Part (Am_Slot_Key key);
Graphical parts added to an object with void Remove_Part (Am_Object part);
Add_Part
will automatically be added to the Am_GRAPHICAL_PARTS
list of the object.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).Get_Part()
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.4.7.2 Layout
Most groups do not use a layout procedure. In these groups, each part has its own left and top, which places it at some user-defined position relative to the left and top of the group.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.4.7.2.1 Vertical and Horizontal Layout
Amulet provides two built-in layout procedures:Am_Formula Am_Vertical_Layout
Am_Formula Am_Horizontal_Layout
These 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. Am_LEFT
and Am_TOP
slots of the graphical parts of the group, overriding whatever values (or formulas) were there before.
Am_X_OFFSET
The horizontal space to leave between the origin of the group and the first part that is placed, measured in number of pixels (default is 0)
Am_Y_OFFSET
Same as Am_X_OFFSET
, only vertical (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, GV 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 6.4 in the Widgets chapter).
Note: the
Am_Resize_Parts_Group
currently scales the parts using integers, so if the size gets very small, the parts will not retain their original proportions, and if the size of the group goes to zero, the objects will stay small forever.4.8 Maps
The 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.7.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.
The next example defines a map whose// Formulas are defined in the global scope, outside of main()
Am_Define_Style_Formula (map_fill_from_rank) {
int rank = self.GV (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);
Am_Point_In_All_Owners
checks whether the point is inside all the owners of object, up to the window. Also validates that all of the owners are visible. If not, returns false. Use this to make sure that the user is not pressing outside of an owner since the other operations do not check this.
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 done automatically by the OS. On all systems, you should specify pathnames with slashes (``/'') as the path separator to avoid machine dependancy.
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_Constraint_Context& cc = *Am_Empty_Constraint_Context);
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. You can provide an
Am_Constraint_Context
parameter inside a formula to make the formula dependent on the relative positions of the objects.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.
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.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.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_DESTROY_WINDOW_METHOD
slot of Am_WINDOW
holds an Am_Object_Method
that is called when the window is destroyed. The default method is:
Am_Default_Window_Destroy_Method
, which destroys the window and exits the main event loop (causes the application to quit) if no windows are left.
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.
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()
.
Am_Screen
can be used in calls to Am_Translate_Coordinates()
to convert from window coordinates to screen coordinates and back again.4.12 Predefined formula constraints
Opal provides a number of constraints that can be put into slots of objects that might be useful. Some of these constraints were described in previous sections.
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_Y_OFFSET
slot.
Am_Fill_To_Right
- Analogous to Am_Fill_To_Bottom
, used in the Am_WIDTH
slot of an object. The Am_X_OFFSET
slot of the object is used to measure the border to the right of the object.
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. 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.
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.
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.
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)
- This slot gets its value from the specified slot (key
) in the same object. Equivalent to { return self.GV(key); }
Am_From_Owner (Am_Slot_Key key)
- This slot gets its value from the specified slot (key
) in the object's owner. Equivalent to { return self.GV_Owner().GV(key); }
Am_From_Part (Am_Slot_Key part, Am_Slot_Key key)
- This slot gets its value from the specified slot (key
) in the specified part (part
) of this object. Equivalent to{ return self.GV_Part(part).GV(key); }
Am_From_Sibling (Am_Slot_Key sibling, Am_Slot_Key key)
- This slot gets its value from the specified slot (key
) in the specified sibling (sibling
) of this object. Equivalent to { return self.GV_Sibling(sibling).GV(key); }